Skip to content

Commit 4b06c22

Browse files
committed
jwt: support ecdsa
1 parent 7f4a53b commit 4b06c22

1 file changed

Lines changed: 74 additions & 22 deletions

File tree

jwt/by_jwt.go

Lines changed: 74 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package jwt
22

33
import (
44
"context"
5+
"crypto"
6+
"crypto/ecdsa"
57
"crypto/rsa"
68
"crypto/x509"
79
"encoding/json"
@@ -32,8 +34,8 @@ var byJwtTlsKeyPaths = sync.OnceValue(func() []string {
3234
const expiryDuration = 30 * 24 * time.Hour
3335

3436
// the first key (most recent version) is used to sign new JWTs
35-
var byPrivateKeys = sync.OnceValue(func() []*rsa.PrivateKey {
36-
keys := []*rsa.PrivateKey{}
37+
var byPrivateKeys = sync.OnceValue(func() []crypto.PrivateKey {
38+
keys := []crypto.PrivateKey{}
3739
glog.Infof("[jwt]paths: %s", byJwtTlsKeyPaths())
3840
errs := []error{}
3941
for _, jwtTlsKeyPath := range byJwtTlsKeyPaths() {
@@ -49,21 +51,27 @@ var byPrivateKeys = sync.OnceValue(func() []*rsa.PrivateKey {
4951
panic(err)
5052
}
5153
block, _ := pem.Decode(bytes)
54+
fmt.Printf("KEY PATH %s\n", path)
5255

5356
keyPathErrs := []error{}
54-
if key, err := x509.ParsePKCS8PrivateKey(block.Bytes); err == nil {
55-
glog.Errorf("[jwt]loaded pkcs8 key \"%s\"\n", path)
56-
keys = append(keys, key.(*rsa.PrivateKey))
57+
if key, err := x509.ParseECPrivateKey(block.Bytes); err == nil {
58+
glog.Errorf("[jwt]loaded ec key \"%s\"\n", path)
59+
keys = append(keys, key)
5760
} else {
58-
keyPathErrs = append(keyPathErrs, err)
59-
if key, err := x509.ParsePKCS1PrivateKey(block.Bytes); err == nil {
60-
glog.Errorf("[jwt]loaded pkcs1 key \"%s\"\n", path)
61+
if key, err := x509.ParsePKCS8PrivateKey(block.Bytes); err == nil {
62+
glog.Errorf("[jwt]loaded pkcs8 key \"%s\"\n", path)
6163
keys = append(keys, key)
6264
} else {
6365
keyPathErrs = append(keyPathErrs, err)
64-
err = errors.Join(keyPathErrs...)
65-
glog.Errorf("[jwt]could not load key \"%s\". err = %s\n", path, err)
66-
errs = append(errs, err)
66+
if key, err := x509.ParsePKCS1PrivateKey(block.Bytes); err == nil {
67+
glog.Errorf("[jwt]loaded pkcs1 key \"%s\"\n", path)
68+
keys = append(keys, key)
69+
} else {
70+
keyPathErrs = append(keyPathErrs, err)
71+
err = errors.Join(keyPathErrs...)
72+
glog.Errorf("[jwt]could not load key \"%s\". err = %s\n", path, err)
73+
errs = append(errs, err)
74+
}
6775
}
6876
}
6977
}
@@ -75,8 +83,24 @@ var byPrivateKeys = sync.OnceValue(func() []*rsa.PrivateKey {
7583
return keys
7684
})
7785

78-
func bySigningKey() *rsa.PrivateKey {
79-
return byPrivateKeys()[0]
86+
func byRsaSigningKey() *rsa.PrivateKey {
87+
for _, key := range byPrivateKeys() {
88+
switch v := key.(type) {
89+
case *rsa.PrivateKey:
90+
return v
91+
}
92+
}
93+
return nil
94+
}
95+
96+
func byEcdsaSigningKey() *ecdsa.PrivateKey {
97+
for _, key := range byPrivateKeys() {
98+
switch v := key.(type) {
99+
case *ecdsa.PrivateKey:
100+
return v
101+
}
102+
}
103+
return nil
80104
}
81105

82106
// the bringyour authorization model is:
@@ -173,13 +197,13 @@ func ParseByJwt(ctx context.Context, jwtSigned string) (*ByJwt, error) {
173197
// todo - ParseWithClaims instead of jwt.Parse
174198
// this will get newly added RegisteredClaims which includes ExipiresAt
175199
token, err = gojwt.Parse(jwtSigned, func(token *gojwt.Token) (any, error) {
176-
return byPrivateKey.Public(), nil
200+
return byPrivateKey.(interface{ Public() crypto.PublicKey }).Public(), nil
177201
}, parserOptions...)
178202
if err == nil {
179203
break
180204
}
181205
}
182-
if err != nil {
206+
if token == nil {
183207
return nil, errors.New("Could not verify signed token.")
184208
}
185209

@@ -258,13 +282,7 @@ func ParseByJwtUnverified(ctx context.Context, jwtStr string) (*ByJwt, error) {
258282
// }
259283

260284
func (self *ByJwt) Sign() string {
261-
262-
token := gojwt.NewWithClaims(gojwt.SigningMethodRS512, self)
263-
jwtSigned, err := token.SignedString(bySigningKey())
264-
if err != nil {
265-
panic(err)
266-
}
267-
return jwtSigned
285+
return sign(self)
268286
}
269287

270288
func (self *ByJwt) Client(deviceId server.Id, clientId server.Id) *ByJwt {
@@ -491,3 +509,37 @@ func IsByJwtActive(ctx context.Context, byJwt *ByJwt) bool {
491509

492510
return !hasInactiveSession
493511
}
512+
513+
func sign(claims gojwt.Claims) string {
514+
var signingMethod gojwt.SigningMethod
515+
var key any
516+
517+
if ecdsaKey := byEcdsaSigningKey(); ecdsaKey != nil {
518+
switch bitLen := ecdsaKey.Curve.Params().N.BitLen(); bitLen {
519+
case 256:
520+
signingMethod = gojwt.SigningMethodES256
521+
case 384:
522+
signingMethod = gojwt.SigningMethodES384
523+
case 512:
524+
signingMethod = gojwt.SigningMethodES512
525+
default:
526+
panic(fmt.Errorf("Unsupported ECDSA bit len %d", bitLen))
527+
}
528+
key = ecdsaKey
529+
} else if rsaKey := byRsaSigningKey(); rsaKey != nil {
530+
if bitLen := rsaKey.N.BitLen(); 2048 <= bitLen {
531+
signingMethod = gojwt.SigningMethodRS512
532+
} else {
533+
panic(fmt.Errorf("Unsupported RSA bit len %d", bitLen))
534+
}
535+
key = rsaKey
536+
} else {
537+
panic(fmt.Errorf("No signing key found"))
538+
}
539+
token := gojwt.NewWithClaims(signingMethod, claims)
540+
jwtSigned, err := token.SignedString(key)
541+
if err != nil {
542+
panic(err)
543+
}
544+
return jwtSigned
545+
}

0 commit comments

Comments
 (0)