-
Notifications
You must be signed in to change notification settings - Fork 373
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: parse jwks in config initialisation
- Loading branch information
1 parent
76096e8
commit f828ede
Showing
3 changed files
with
152 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
package conf | ||
|
||
import ( | ||
"crypto/ecdsa" | ||
"crypto/ed25519" | ||
"crypto/rsa" | ||
"crypto/x509" | ||
"encoding/base64" | ||
"encoding/json" | ||
"fmt" | ||
|
||
"github.com/lestrrat-go/jwx/v2/jwk" | ||
) | ||
|
||
type JwtKeysDecoder map[string]JwkInfo | ||
|
||
type JwkInfo struct { | ||
PublicKey jwk.Key `json:"public_key"` | ||
PrivateKey jwk.Key `json:"private_key"` | ||
} | ||
|
||
type KeyInfo struct { | ||
Type string `json:"type"` | ||
PrivateKey string `json:"private_key"` | ||
InUse bool `json:"in_use"` | ||
} | ||
|
||
// Decode implements the Decoder interface | ||
// which transforms the keys stored as der binary strings into jwks | ||
func (j *JwtKeysDecoder) Decode(value string) error { | ||
data := map[string]KeyInfo{} | ||
if err := json.Unmarshal([]byte(value), &data); err != nil { | ||
return err | ||
} | ||
|
||
config := JwtKeysDecoder{} | ||
for kid, key := range data { | ||
// all private keys should be stored as der binary strings in base64 | ||
derBytes, err := base64.StdEncoding.DecodeString(key.PrivateKey) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
var privKey any | ||
if key.Type == "hmac" { | ||
privKey = derBytes | ||
} else { | ||
// assume key is asymmetric | ||
privKey, err = x509.ParsePKCS8PrivateKey(derBytes) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
privJwk, err := jwk.FromRaw(privKey) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
pubJwk, err := jwk.PublicKeyOf(privJwk) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
alg := getAlg(privKey) | ||
if alg == "" { | ||
return fmt.Errorf("unsupported key alg: %v", kid) | ||
} | ||
// Set kid and alg claims for private key | ||
privJwk.Set(jwk.KeyIDKey, kid) | ||
privJwk.Set(jwk.AlgorithmKey, alg) | ||
|
||
// Set kid, alg, use claims for public key | ||
pubJwk.Set(jwk.KeyIDKey, kid) | ||
pubJwk.Set(jwk.KeyUsageKey, "sig") | ||
pubJwk.Set(jwk.AlgorithmKey, alg) | ||
|
||
config[kid] = JwkInfo{ | ||
PublicKey: pubJwk, | ||
PrivateKey: privJwk, | ||
} | ||
} | ||
*j = config | ||
return nil | ||
} | ||
|
||
func (j *JwtKeysDecoder) Validate() error { | ||
// Validate performs _minimal_ checks if the data stored in the key are valid. | ||
// By minimal, we mean that it does not check if the key is valid for use in | ||
// cryptographic operations. For example, it does not check if an RSA key's | ||
// `e` field is a valid exponent, or if the `n` field is a valid modulus. | ||
// Instead, it checks for things such as the _presence_ of some required fields, | ||
// or if certain keys' values are of particular length. | ||
// | ||
// Note that depending on th underlying key type, use of this method requires | ||
// that multiple fields in the key are properly populated. For example, an EC | ||
// key's "x", "y" fields cannot be validated unless the "crv" field is populated first. | ||
for _, key := range *j { | ||
if err := key.PrivateKey.Validate(); err != nil { | ||
return err | ||
} | ||
if err := key.PublicKey.Validate(); err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func getAlg(key any) string { | ||
var alg string | ||
switch p := key.(type) { | ||
case []byte: | ||
alg = "HS256" | ||
case *ecdsa.PrivateKey: | ||
switch p.Curve.Params().Name { | ||
case "P-256": | ||
alg = "ES256" | ||
case "P-384": | ||
alg = "ES384" | ||
case "P-521": | ||
alg = "ES512" | ||
} | ||
case *rsa.PrivateKey: | ||
switch p.N.BitLen() { | ||
case 2048: | ||
alg = "RS256" | ||
case 4096: | ||
alg = "RS512" | ||
} | ||
case *ed25519.PrivateKey: | ||
// Ed25519 is still experimental based on https://github.com/lestrrat-go/jwx/tree/develop/v2/jwk#supported-key-types | ||
alg = "EdDSA" | ||
default: | ||
return "" | ||
} | ||
return alg | ||
} |