361 lines
7.9 KiB
Go
361 lines
7.9 KiB
Go
package jwk
|
|
|
|
import (
|
|
"crypto"
|
|
"crypto/rsa"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"math/big"
|
|
"reflect"
|
|
|
|
"github.com/lestrrat-go/jwx/v3/internal/base64"
|
|
"github.com/lestrrat-go/jwx/v3/internal/pool"
|
|
"github.com/lestrrat-go/jwx/v3/jwa"
|
|
)
|
|
|
|
func init() {
|
|
RegisterKeyExporter(jwa.RSA(), KeyExportFunc(rsaJWKToRaw))
|
|
}
|
|
|
|
func (k *rsaPrivateKey) Import(rawKey *rsa.PrivateKey) error {
|
|
k.mu.Lock()
|
|
defer k.mu.Unlock()
|
|
|
|
d, err := bigIntToBytes(rawKey.D)
|
|
if err != nil {
|
|
return fmt.Errorf(`invalid rsa.PrivateKey: %w`, err)
|
|
}
|
|
k.d = d
|
|
|
|
l := len(rawKey.Primes)
|
|
|
|
if l < 0 /* I know, I'm being paranoid */ || l > 2 {
|
|
return fmt.Errorf(`invalid number of primes in rsa.PrivateKey: need 0 to 2, but got %d`, len(rawKey.Primes))
|
|
}
|
|
|
|
if l > 0 {
|
|
p, err := bigIntToBytes(rawKey.Primes[0])
|
|
if err != nil {
|
|
return fmt.Errorf(`invalid rsa.PrivateKey: %w`, err)
|
|
}
|
|
k.p = p
|
|
}
|
|
|
|
if l > 1 {
|
|
q, err := bigIntToBytes(rawKey.Primes[1])
|
|
if err != nil {
|
|
return fmt.Errorf(`invalid rsa.PrivateKey: %w`, err)
|
|
}
|
|
k.q = q
|
|
}
|
|
|
|
// dp, dq, qi are optional values
|
|
if v, err := bigIntToBytes(rawKey.Precomputed.Dp); err == nil {
|
|
k.dp = v
|
|
}
|
|
if v, err := bigIntToBytes(rawKey.Precomputed.Dq); err == nil {
|
|
k.dq = v
|
|
}
|
|
if v, err := bigIntToBytes(rawKey.Precomputed.Qinv); err == nil {
|
|
k.qi = v
|
|
}
|
|
|
|
// public key part
|
|
n, e, err := importRsaPublicKeyByteValues(&rawKey.PublicKey)
|
|
if err != nil {
|
|
return fmt.Errorf(`invalid rsa.PrivateKey: %w`, err)
|
|
}
|
|
k.n = n
|
|
k.e = e
|
|
|
|
return nil
|
|
}
|
|
|
|
func importRsaPublicKeyByteValues(rawKey *rsa.PublicKey) ([]byte, []byte, error) {
|
|
n, err := bigIntToBytes(rawKey.N)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf(`invalid rsa.PublicKey: %w`, err)
|
|
}
|
|
|
|
data := make([]byte, 8)
|
|
binary.BigEndian.PutUint64(data, uint64(rawKey.E))
|
|
i := 0
|
|
for ; i < len(data); i++ {
|
|
if data[i] != 0x0 {
|
|
break
|
|
}
|
|
}
|
|
return n, data[i:], nil
|
|
}
|
|
|
|
func (k *rsaPublicKey) Import(rawKey *rsa.PublicKey) error {
|
|
k.mu.Lock()
|
|
defer k.mu.Unlock()
|
|
|
|
n, e, err := importRsaPublicKeyByteValues(rawKey)
|
|
if err != nil {
|
|
return fmt.Errorf(`invalid rsa.PrivateKey: %w`, err)
|
|
}
|
|
k.n = n
|
|
k.e = e
|
|
|
|
return nil
|
|
}
|
|
|
|
func buildRSAPublicKey(key *rsa.PublicKey, n, e []byte) {
|
|
bin := pool.BigInt().Get()
|
|
bie := pool.BigInt().Get()
|
|
defer pool.BigInt().Put(bie)
|
|
|
|
bin.SetBytes(n)
|
|
bie.SetBytes(e)
|
|
|
|
key.N = bin
|
|
key.E = int(bie.Int64())
|
|
}
|
|
|
|
var rsaConvertibleKeys = []reflect.Type{
|
|
reflect.TypeFor[RSAPrivateKey](),
|
|
reflect.TypeFor[RSAPublicKey](),
|
|
}
|
|
|
|
func rsaJWKToRaw(key Key, hint any) (any, error) {
|
|
extracted, err := extractEmbeddedKey(key, rsaConvertibleKeys)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(`failed to extract embedded key: %w`, err)
|
|
}
|
|
switch key := extracted.(type) {
|
|
case RSAPrivateKey:
|
|
switch hint.(type) {
|
|
case *rsa.PrivateKey, *any:
|
|
default:
|
|
return nil, fmt.Errorf(`invalid destination object type %T for private RSA JWK: %w`, hint, ContinueError())
|
|
}
|
|
|
|
locker, ok := key.(rlocker)
|
|
if !ok {
|
|
locker.rlock()
|
|
defer locker.runlock()
|
|
}
|
|
|
|
od, ok := key.D()
|
|
if !ok {
|
|
return nil, fmt.Errorf(`missing "d" value`)
|
|
}
|
|
|
|
oq, ok := key.Q()
|
|
if !ok {
|
|
return nil, fmt.Errorf(`missing "q" value`)
|
|
}
|
|
|
|
op, ok := key.P()
|
|
if !ok {
|
|
return nil, fmt.Errorf(`missing "p" value`)
|
|
}
|
|
|
|
var d, q, p big.Int // note: do not use from sync.Pool
|
|
|
|
d.SetBytes(od)
|
|
q.SetBytes(oq)
|
|
p.SetBytes(op)
|
|
|
|
// optional fields
|
|
var dp, dq, qi *big.Int
|
|
|
|
if odp, ok := key.DP(); ok {
|
|
dp = &big.Int{} // note: do not use from sync.Pool
|
|
dp.SetBytes(odp)
|
|
}
|
|
|
|
if odq, ok := key.DQ(); ok {
|
|
dq = &big.Int{} // note: do not use from sync.Pool
|
|
dq.SetBytes(odq)
|
|
}
|
|
|
|
if oqi, ok := key.QI(); ok {
|
|
qi = &big.Int{} // note: do not use from sync.Pool
|
|
qi.SetBytes(oqi)
|
|
}
|
|
|
|
n, ok := key.N()
|
|
if !ok {
|
|
return nil, fmt.Errorf(`missing "n" value`)
|
|
}
|
|
|
|
e, ok := key.E()
|
|
if !ok {
|
|
return nil, fmt.Errorf(`missing "e" value`)
|
|
}
|
|
|
|
var privkey rsa.PrivateKey
|
|
buildRSAPublicKey(&privkey.PublicKey, n, e)
|
|
privkey.D = &d
|
|
privkey.Primes = []*big.Int{&p, &q}
|
|
|
|
if dp != nil {
|
|
privkey.Precomputed.Dp = dp
|
|
}
|
|
if dq != nil {
|
|
privkey.Precomputed.Dq = dq
|
|
}
|
|
if qi != nil {
|
|
privkey.Precomputed.Qinv = qi
|
|
}
|
|
// This may look like a no-op, but it's required if we want to
|
|
// compare it against a key generated by rsa.GenerateKey
|
|
privkey.Precomputed.CRTValues = []rsa.CRTValue{}
|
|
return &privkey, nil
|
|
case RSAPublicKey:
|
|
switch hint.(type) {
|
|
case *rsa.PublicKey, *any:
|
|
default:
|
|
return nil, fmt.Errorf(`invalid destination object type %T for public RSA JWK: %w`, hint, ContinueError())
|
|
}
|
|
|
|
locker, ok := key.(rlocker)
|
|
if !ok {
|
|
locker.rlock()
|
|
defer locker.runlock()
|
|
}
|
|
|
|
n, ok := key.N()
|
|
if !ok {
|
|
return nil, fmt.Errorf(`missing "n" value`)
|
|
}
|
|
|
|
e, ok := key.E()
|
|
if !ok {
|
|
return nil, fmt.Errorf(`missing "e" value`)
|
|
}
|
|
|
|
var pubkey rsa.PublicKey
|
|
buildRSAPublicKey(&pubkey, n, e)
|
|
|
|
return &pubkey, nil
|
|
|
|
default:
|
|
return nil, ContinueError()
|
|
}
|
|
}
|
|
|
|
func makeRSAPublicKey(src Key) (Key, error) {
|
|
newKey := newRSAPublicKey()
|
|
|
|
// Iterate and copy everything except for the bits that should not be in the public key
|
|
for _, k := range src.Keys() {
|
|
switch k {
|
|
case RSADKey, RSADPKey, RSADQKey, RSAPKey, RSAQKey, RSAQIKey:
|
|
continue
|
|
default:
|
|
var v any
|
|
if err := src.Get(k, &v); err != nil {
|
|
return nil, fmt.Errorf(`rsa: makeRSAPublicKey: failed to get field %q: %w`, k, err)
|
|
}
|
|
if err := newKey.Set(k, v); err != nil {
|
|
return nil, fmt.Errorf(`rsa: makeRSAPublicKey: failed to set field %q: %w`, k, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
return newKey, nil
|
|
}
|
|
|
|
func (k *rsaPrivateKey) PublicKey() (Key, error) {
|
|
return makeRSAPublicKey(k)
|
|
}
|
|
|
|
func (k *rsaPublicKey) PublicKey() (Key, error) {
|
|
return makeRSAPublicKey(k)
|
|
}
|
|
|
|
// Thumbprint returns the JWK thumbprint using the indicated
|
|
// hashing algorithm, according to RFC 7638
|
|
func (k rsaPrivateKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
|
|
k.mu.RLock()
|
|
defer k.mu.RUnlock()
|
|
|
|
var key rsa.PrivateKey
|
|
if err := Export(&k, &key); err != nil {
|
|
return nil, fmt.Errorf(`failed to export RSA private key: %w`, err)
|
|
}
|
|
return rsaThumbprint(hash, &key.PublicKey)
|
|
}
|
|
|
|
func (k rsaPublicKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
|
|
k.mu.RLock()
|
|
defer k.mu.RUnlock()
|
|
|
|
var key rsa.PublicKey
|
|
if err := Export(&k, &key); err != nil {
|
|
return nil, fmt.Errorf(`failed to export RSA public key: %w`, err)
|
|
}
|
|
return rsaThumbprint(hash, &key)
|
|
}
|
|
|
|
func rsaThumbprint(hash crypto.Hash, key *rsa.PublicKey) ([]byte, error) {
|
|
buf := pool.BytesBuffer().Get()
|
|
defer pool.BytesBuffer().Put(buf)
|
|
|
|
buf.WriteString(`{"e":"`)
|
|
buf.WriteString(base64.EncodeUint64ToString(uint64(key.E)))
|
|
buf.WriteString(`","kty":"RSA","n":"`)
|
|
buf.WriteString(base64.EncodeToString(key.N.Bytes()))
|
|
buf.WriteString(`"}`)
|
|
|
|
h := hash.New()
|
|
if _, err := buf.WriteTo(h); err != nil {
|
|
return nil, fmt.Errorf(`failed to write rsaThumbprint: %w`, err)
|
|
}
|
|
return h.Sum(nil), nil
|
|
}
|
|
|
|
func validateRSAKey(key interface {
|
|
N() ([]byte, bool)
|
|
E() ([]byte, bool)
|
|
}, checkPrivate bool) error {
|
|
n, ok := key.N()
|
|
if !ok {
|
|
return fmt.Errorf(`missing "n" value`)
|
|
}
|
|
|
|
e, ok := key.E()
|
|
if !ok {
|
|
return fmt.Errorf(`missing "e" value`)
|
|
}
|
|
|
|
if len(n) == 0 {
|
|
// Ideally we would like to check for the actual length, but unlike
|
|
// EC keys, we have nothing in the key itself that will tell us
|
|
// how many bits this key should have.
|
|
return fmt.Errorf(`missing "n" value`)
|
|
}
|
|
if len(e) == 0 {
|
|
return fmt.Errorf(`missing "e" value`)
|
|
}
|
|
if checkPrivate {
|
|
if priv, ok := key.(keyWithD); ok {
|
|
if d, ok := priv.D(); !ok || len(d) == 0 {
|
|
return fmt.Errorf(`missing "d" value`)
|
|
}
|
|
} else {
|
|
return fmt.Errorf(`missing "d" value`)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (k *rsaPrivateKey) Validate() error {
|
|
if err := validateRSAKey(k, true); err != nil {
|
|
return NewKeyValidationError(fmt.Errorf(`jwk.RSAPrivateKey: %w`, err))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (k *rsaPublicKey) Validate() error {
|
|
if err := validateRSAKey(k, false); err != nil {
|
|
return NewKeyValidationError(fmt.Errorf(`jwk.RSAPublicKey: %w`, err))
|
|
}
|
|
return nil
|
|
}
|