Home > Software engineering >  expose public JWK in Go
expose public JWK in Go

Time:01-07

I am trying to expose jwks endpoint in Go but it appears the way I am generating the modulus is not correct. Has anyone already done it and can tell me what I am 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"
        }
    ]
}

CodePudding user response:

According to the spec, 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 spec above, you probably want to use urlencoding without any padding.

 base64.RawURLEncoding.EncodeToString(...)
  • Related