在Go中公开JWK(JSON Web Key)

huangapple go评论81阅读模式
英文:

Expose public JWK in Go

问题

我正在尝试在Go中公开一个JWK的端点,但似乎我生成模数的方式不正确。我做错了什么?

以下是我生成密钥的步骤:

openssl genrsa -out private_key.pem 2048

然后生成公钥部分:

openssl rsa -in private_key.pem -pubout -out public_key.pub

在我的Go程序中,我这样做:

var verifyKey *rsa.PublicKey
verifyBytes, err := ioutil.ReadFile("public_key.pub")
verifyKey, err = jwt.ParseRSAPublicKeyFromPEM(verifyBytes)

modulus := base64.StdEncoding.EncodeToString((*verifyKey.N).Bytes())
exponent := base64.StdEncoding.EncodeToString(big.NewInt(int64(verifyKey.E)).Bytes())

这是如何公开密钥的:

func (s *ExtAuthzServer) ServeHTTP(response http.ResponseWriter, request *http.Request) {
    if request.URL.Path == "/.well-known/jwks.json" {
        log.Printf("[HTTP] jwks requested")
        n := base64.StdEncoding.EncodeToString((*verifyKey.N).Bytes())
        e := base64.StdEncoding.EncodeToString(big.NewInt(int64(verifyKey.E)).Bytes())
        keys := PublicKeysData{
            Keys: []KeyData{KeyData{"RSA", "go-ext-authz", "sig", n, e}},
        }
        response.WriteHeader(http.StatusOK)
        b, _ := json.Marshal(keys)
        _, _ = response.Write([]byte(b))
        return
    }
}

使用以下结构定义:

type PublicKeysData struct {
    Keys []KeyData `json:"keys"`
}

type KeyData struct {
    Kty string `json:"kty"`
    Kid string `json:"kid"`
    Use string `json:"use"`
    N   string `json:"n"`
    E   string `json:"e"`
}

以下是curl(或者说HTTPie)的输出:

HTTP/1.1 200 OK
Content-Length: 419
Content-Type: text/plain; charset=utf-8
Date: Fri, 06 Jan 2023 16:35:48 GMT

{
    "keys": [
        {
            "e": "AQAB",
            "kid": "go-ext-authz",
            "kty": "RSA",
            "n": "pulIwmeoYdXIOS+vPMURqJsB2IhL3G+OIgMm8I7FqwgeM1Rf12kxycb8VbAVgaN+cMsVfFzxg+oiUqHW4af6dO503bNgZ88DemO/gT9J9Ob4EcmNNohVX28ts6qRmhOtTN0o4xV3cHXiJYL+JTf3U/GhyEK8bJcIgj1X8kNhl7X3gtza2Ft5S8t61ZepdQJdDIdzq7wpw2DTRJ76rvstOvzvLNjfhPhX48aFaw0tSJKw2LmoawHvUviP6tjro7gFUmLX6xolniv/1U/Uas8ZbNFPZBbUs1mjMccNErtUi02VZuHWqGtHL8v+n7rgso9NMd/ljU+BV/dB2KWnO6dD2Q==",
            "use": "sig"
        }
    ]
}
英文:

I am trying to expose a JWK's endpoint in Go, but it appears the way I am generating the modulus is not correct. What am I doing wrong?

Here is how I have generated my keys:

openssl genrsa -out private_key.pem 2048

And the public part:

openssl rsa -in private_key.pem -pubout -out public_key.pub

In my Go program here is what I do:

var verifyKey *rsa.PublicKey
verifyBytes, err := ioutil.ReadFile("public_key.pub")
verifyKey, err = jwt.ParseRSAPublicKeyFromPEM(verifyBytes)

modulus := base64.StdEncoding.EncodeToString((*verifyKey.N).Bytes())
exponent := base64.StdEncoding.EncodeToString(big.NewInt(int64(verifyKey.E)).Bytes())

And here is how the key is exposed:

func (s *ExtAuthzServer) ServeHTTP(response http.ResponseWriter, request *http.Request) {
    if request.URL.Path == "/.well-known/jwks.json" {
        log.Printf("[HTTP] jwks requested")
        n := base64.StdEncoding.EncodeToString((*verifyKey.N).Bytes())
        e := base64.StdEncoding.EncodeToString(big.NewInt(int64(verifyKey.E)).Bytes())
        keys := PublicKeysData{
            Keys: []KeyData{KeyData{"RSA", "go-ext-authz", "sig", n, e}},
        }
        response.WriteHeader(http.StatusOK)
        b, _ := json.Marshal(keys)
        _, _ = response.Write([]byte(b))
        return
    }
}

with the following struct definition

type PublicKeysData struct {
    Keys []KeyData `json:"keys"`
}

type KeyData struct {
    Kty string `json:"kty"`
    Kid string `json:"kid"`
    Use string `json:"use"`
    N   string `json:"n"`
    E   string `json:"e"`
}

And here is the output of a curl (I mean a HTTPie):

HTTP/1.1 200 OK
Content-Length: 419
Content-Type: text/plain; charset=utf-8
Date: Fri, 06 Jan 2023 16:35:48 GMT

{
    "keys": [
        {
            "e": "AQAB",
            "kid": "go-ext-authz",
            "kty": "RSA",
            "n": "pulIwmeoYdXIOS+vPMURqJsB2IhL3G+OIgMm8I7FqwgeM1Rf12kxycb8VbAVgaN+cMsVfFzxg+oiUqHW4af6dO503bNgZ88DemO/gT9J9Ob4EcmNNohVX28ts6qRmhOtTN0o4xV3cHXiJYL+JTf3U/GhyEK8bJcIgj1X8kNhl7X3gtza2Ft5S8t61ZepdQJdDIdzq7wpw2DTRJ76rvstOvzvLNjfhPhX48aFaw0tSJKw2LmoawHvUviP6tjro7gFUmLX6xolniv/1U/Uas8ZbNFPZBbUs1mjMccNErtUi02VZuHWqGtHL8v+n7rgso9NMd/ljU+BV/dB2KWnO6dD2Q==",
            "use": "sig"
        }
    ]
}

答案1

得分: 2

根据规范,您不能使用标准的Base64编码:

6.3.1.1. "n" (Modulus) 参数

"n" (模数) 参数包含 RSA 公钥的模数值。它表示为 Base64urlUInt 编码的值。

请注意,实施者发现一些加密库在返回模数表示时,在模数表示前面添加了一个额外的零值字节,例如,对于一个 2048 位的密钥,返回的字节为 257,而不是 256。使用此类库的实现需要小心从 base64url 编码的表示中省略额外的字节。

6.3.1.2. "e" (Exponent) 参数

"e" (指数) 参数包含 RSA 公钥的指数值。它表示为 Base64urlUInt 编码的值。

例如,当表示值 65537 时,要进行 base64url 编码的字节序列必须由三个字节 [1, 0, 1] 组成;该值的结果表示为 "AQAB"。

Go 有urlEncoding,如果需要,还可以额外配置以使用填充。

根据上述规范,您可能希望使用没有任何填充的URL 编码

base64.RawURLEncoding.EncodeToString(...)
英文:

According to the specification, you cannot use standard Base64 encoding:

> 6.3.1.1. "n" (Modulus) Parameter
>
> The "n" (modulus) parameter contains the modulus value for the RSA
public key. It is represented as a Base64urlUInt-encoded value.
>
> Note that implementers have found that some cryptographic libraries
prefix an extra zero-valued octet to the modulus representations they
return, for instance, returning 257 octets for a 2048-bit key, rather
than 256. Implementations using such libraries will need to take
care to omit the extra octet from the base64url-encoded
representation.
>
> 6.3.1.2. "e" (Exponent) Parameter
>
> The "e" (exponent) parameter contains the exponent value for the RSA
public key. It is represented as a Base64urlUInt-encoded value.
>
> For instance, when representing the value 65537, the octet sequence
to be base64url-encoded MUST consist of the three octets [1, 0, 1];
the resulting representation for this value is "AQAB".

Go has urlEncoding which can be additionally configured to use padding, if needed.

Reading the specification above, you probably want to use URL encoding without any padding.

 base64.RawURLEncoding.EncodeToString(...)

huangapple
  • 本文由 发表于 2023年1月6日 20:57:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/75031229.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定