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"
}
]
}
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(...)