-
Notifications
You must be signed in to change notification settings - Fork 31
/
jwk.go
124 lines (113 loc) · 2.86 KB
/
jwk.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package utils
import (
"context"
"crypto"
"crypto/rand"
"crypto/rsa"
"encoding/base64"
"fmt"
"time"
"github.com/google/uuid"
"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/jwt"
"github.com/lestrrat-go/jwx/v2/jwk"
)
const (
RSAKeySize = 2048
)
func CreateJWKs(numOfKeys int) (jwk.Set, error) {
keySet := jwk.NewSet()
for ; numOfKeys > 0; numOfKeys-- {
// generate keys
keyRaw, err := rsa.GenerateKey(rand.Reader, RSAKeySize)
if err != nil {
return nil, err
}
rsaKey, err := jwk.FromRaw(keyRaw)
if err != nil {
return nil, err
}
pubKey, err := rsaKey.PublicKey()
if err != nil {
return nil, err
}
thumb, err := pubKey.Thumbprint(crypto.SHA256)
if err != nil {
return nil, err
}
if err := rsaKey.Set(jwk.AlgorithmKey, "RS256"); err != nil {
return nil, err
}
if err := rsaKey.Set(jwk.KeyUsageKey, "sig"); err != nil {
return nil, err
}
if err := rsaKey.Set(jwk.KeyIDKey, base64.RawURLEncoding.EncodeToString(thumb)); err != nil {
return nil, err
}
if err := keySet.AddKey(rsaKey); err != nil {
return nil, err
}
}
return keySet, nil
}
func CreateJWKWithKID(id string) (jwk.Key, error) {
// generate key
keyRaw, err := rsa.GenerateKey(rand.Reader, RSAKeySize)
if err != nil {
return nil, err
}
rsaKey, err := jwk.FromRaw(keyRaw)
if err != nil {
return nil, err
}
if err := rsaKey.Set(jwk.AlgorithmKey, "RS256"); err != nil {
return nil, err
}
if err := rsaKey.Set(jwk.KeyUsageKey, "sig"); err != nil {
return nil, err
}
if err := rsaKey.Set(jwk.KeyIDKey, id); err != nil {
return nil, err
}
return rsaKey, nil
}
// GetPublicKeySet convert private to public
func GetPublicKeySet(ctx context.Context, privateKeySet jwk.Set) (jwk.Set, error) {
publicKeySet := jwk.NewSet()
for iter := privateKeySet.Keys(ctx); iter.Next(ctx); {
pair := iter.Pair()
key := pair.Value.(jwk.Key)
pubKey, err := key.PublicKey()
if err != nil {
return nil, fmt.Errorf("failed to generate public key from private rsa: %w", err)
}
if err := publicKeySet.AddKey(pubKey); err != nil {
return nil, err
}
}
return publicKeySet, nil
}
// BuildToken creates a signed jwt using provided private key
// Ensure the key contains kid else the operation fails
func BuildToken(rsaKey jwk.Key, issuer, sub string,
validity time.Duration, customClaims map[string]string) ([]byte, error) {
if rsaKey.KeyID() == "" {
return nil, fmt.Errorf("key id is empty")
}
body := jwt.NewBuilder().
Issuer(issuer).
IssuedAt(time.Now().UTC()).
NotBefore(time.Now().UTC()).
Expiration(time.Now().UTC().Add(validity)).
JwtID(uuid.New().String()).
Subject(sub)
body.Claim(jwk.KeyIDKey, rsaKey.KeyID())
for claimKey, claimVal := range customClaims {
body = body.Claim(claimKey, claimVal)
}
tok, err := body.Build()
if err != nil {
return nil, err
}
return jwt.Sign(tok, jwt.WithKey(jwa.RS256, rsaKey))
}