1# JWK [](https://pkg.go.dev/github.com/lestrrat-go/jwx/jwk)
2
3Package jwk implements JWK as described in [RFC7517](https://tools.ietf.org/html/rfc7517)
4
5* Parse and work with RSA/EC/Symmetric/OKP JWK types
6 * Convert to and from JSON
7 * Convert to and from raw key types (e.g. *rsa.PrivateKey)
8* Ability to keep a JWKS fresh.
9
10How-to style documentation can be found in the [docs directory](../docs).
11
12Examples are located in the examples directory ([jwk_example_test.go](../examples/jwk_example_test.go))
13
14Supported key types:
15
16| kty | Curve | Go Key Type |
17|:----|:------------------------|:----------------------------------------------|
18| RSA | N/A | rsa.PrivateKey / rsa.PublicKey (2) |
19| EC | P-256<br>P-384<br>P-521<br>secp256k1 (1) | ecdsa.PrivateKey / ecdsa.PublicKey (2) |
20| oct | N/A | []byte |
21| OKP | Ed25519 (1) | ed25519.PrivateKey / ed25519.PublicKey (2) |
22| | X25519 (1) | (jwx/)x25519.PrivateKey / x25519.PublicKey (2)|
23
24* Note 1: Experimental
25* Note 2: Either value or pointers accepted (e.g. rsa.PrivateKey or *rsa.PrivateKey)
26
27# SYNOPSIS
28
29## Parse a JWK or a JWK set
30
31```go
32 // Parse a single JWK key.
33 key, err := jwk.ParseKey(...)
34
35 // Parse a JWK set (or a single JWK key)
36 set, err := jwk.Parse(...)
37```
38
39## Create JWK keys from raw keys
40
41```go
42func ExampleNew() {
43 // New returns different underlying types of jwk.Key objects
44 // depending on the input value.
45
46 // []byte -> jwk.SymmetricKey
47 {
48 raw := []byte("Lorem Ipsum")
49 key, err := jwk.New(raw)
50 if err != nil {
51 fmt.Printf("failed to create symmetric key: %s\n", err)
52 return
53 }
54 if _, ok := key.(jwk.SymmetricKey); !ok {
55 fmt.Printf("expected jwk.SymmetricKey, got %T\n", key)
56 return
57 }
58 }
59
60 // *rsa.PrivateKey -> jwk.RSAPrivateKey
61 // *rsa.PublicKey -> jwk.RSAPublicKey
62 {
63 raw, err := rsa.GenerateKey(rand.Reader, 2048)
64 if err != nil {
65 fmt.Printf("failed to generate new RSA privatre key: %s\n", err)
66 return
67 }
68
69 key, err := jwk.New(raw)
70 if err != nil {
71 fmt.Printf("failed to create symmetric key: %s\n", err)
72 return
73 }
74 if _, ok := key.(jwk.RSAPrivateKey); !ok {
75 fmt.Printf("expected jwk.SymmetricKey, got %T\n", key)
76 return
77 }
78 // PublicKey is omitted for brevity
79 }
80
81 // *ecdsa.PrivateKey -> jwk.ECDSAPrivateKey
82 // *ecdsa.PublicKey -> jwk.ECDSAPublicKey
83 {
84 raw, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
85 if err != nil {
86 fmt.Printf("failed to generate new ECDSA privatre key: %s\n", err)
87 return
88 }
89
90 key, err := jwk.New(raw)
91 if err != nil {
92 fmt.Printf("failed to create symmetric key: %s\n", err)
93 return
94 }
95 if _, ok := key.(jwk.ECDSAPrivateKey); !ok {
96 fmt.Printf("expected jwk.SymmetricKey, got %T\n", key)
97 return
98 }
99 // PublicKey is omitted for brevity
100 }
101
102 // OUTPUT:
103}
104```
105
106# Get the JSON representation of a key
107
108```go
109func ExampleMarshalJSON() {
110 // to get the same values every time, we need to create a static source
111 // of "randomness"
112 rdr := bytes.NewReader([]byte("01234567890123456789012345678901234567890123456789ABCDEF"))
113 raw, err := ecdsa.GenerateKey(elliptic.P384(), rdr)
114 if err != nil {
115 fmt.Printf("failed to generate new ECDSA privatre key: %s\n", err)
116 return
117 }
118
119 key, err := jwk.New(raw)
120 if err != nil {
121 fmt.Printf("failed to create symmetric key: %s\n", err)
122 return
123 }
124 if _, ok := key.(jwk.ECDSAPrivateKey); !ok {
125 fmt.Printf("expected jwk.SymmetricKey, got %T\n", key)
126 return
127 }
128
129 key.Set(jwk.KeyIDKey, "mykey")
130
131 buf, err := json.MarshalIndent(key, "", " ")
132 if err != nil {
133 fmt.Printf("failed to marshal key into JSON: %s\n", err)
134 return
135 }
136 fmt.Printf("%s\n", buf)
137
138 // OUTPUT:
139 // {
140 // "kty": "EC",
141 // "crv": "P-384",
142 // "d": "ODkwMTIzNDU2Nzg5MDEyMz7deMbyLt8g4cjcxozuIoygLLlAeoQ1AfM9TSvxkFHJ",
143 // "kid": "mykey",
144 // "x": "gvvRMqm1w5aHn7sVNA2QUJeOVcedUnmiug6VhU834gzS9k87crVwu9dz7uLOdoQl",
145 // "y": "7fVF7b6J_6_g6Wu9RuJw8geWxEi5ja9Gp2TSdELm5u2E-M7IF-bsxqcdOj3n1n7N"
146 // }
147}
148```
149
150# Auto-Refresh a key during a long running process
151
152```go
153func ExampleAutoRefresh() {
154 ctx, cancel := context.WithCancel(context.Background())
155
156 const googleCerts = `https://www.googleapis.com/oauth2/v3/certs`
157 ar := jwk.NewAutoRefresh(ctx)
158
159 // Tell *jwk.AutoRefresh that we only want to refresh this JWKS
160 // when it needs to (based on Cache-Control or Expires header from
161 // the HTTP response). If the calculated minimum refresh interval is less
162 // than 15 minutes, don't go refreshing any earlier than 15 minutes.
163 ar.Configure(googleCerts, jwk.WithMinRefreshInterval(15*time.Minute))
164
165 // Refresh the JWKS once before getting into the main loop.
166 // This allows you to check if the JWKS is available before we start
167 // a long-running program
168 _, err := ar.Refresh(ctx, googleCerts)
169 if err != nil {
170 fmt.Printf("failed to refresh google JWKS: %s\n", err)
171 return
172 }
173
174 // Pretend that this is your program's main loop
175MAIN:
176 for {
177 select {
178 case <-ctx.Done():
179 break MAIN
180 default:
181 }
182 keyset, err := ar.Fetch(ctx, googleCerts)
183 if err != nil {
184 fmt.Printf("failed to fetch google JWKS: %s\n", err)
185 return
186 }
187 _ = keyset
188
189 // Do interesting stuff with the keyset... but here, we just
190 // sleep for a bit
191 time.Sleep(time.Second)
192
193 // Because we're a dummy program, we just cancel the loop now.
194 // If this were a real program, you prosumably loop forever
195 cancel()
196 }
197 // OUTPUT:
198}
199```
200
201Parse and use a JWK key:
202
203```go
204
205import (
206 "encoding/json"
207 "log"
208
209 "github.com/lestrrat-go/jwx/jwk"
210)
211
212func main() {
213 set, err := jwk.Fetch(context.Background(), "https://www.googleapis.com/oauth2/v3/certs")
214 if err != nil {
215 log.Printf("failed to parse JWK: %s", err)
216 return
217 }
218
219 // Key sets can be serialized back to JSON
220 {
221 jsonbuf, err := json.Marshal(set)
222 if err != nil {
223 log.Printf("failed to marshal key set into JSON: %s", err)
224 return
225 }
226 log.Printf("%s", jsonbuf)
227 }
228
229 for it := set.Iterate(context.Background()); it.Next(context.Background()); {
230 pair := it.Pair()
231 key := pair.Value.(jwk.Key)
232
233 var rawkey interface{} // This is the raw key, like *rsa.PrivateKey or *ecdsa.PrivateKey
234 if err := key.Raw(&rawkey); err != nil {
235 log.Printf("failed to create public key: %s", err)
236 return
237 }
238 // Use rawkey for jws.Verify() or whatever.
239 _ = rawkey
240
241 // You can create jwk.Key from a raw key, too
242 fromRawKey, err := jwk.New(rawkey)
243
244
245 // Keys can be serialized back to JSON
246 jsonbuf, err := json.Marshal(key)
247 if err != nil {
248 log.Printf("failed to marshal key into JSON: %s", err)
249 return
250 }
251 log.Printf("%s", jsonbuf)
252
253 // If you know the underlying Key type (RSA, EC, Symmetric), you can
254 // create an empy instance first
255 // key := jwk.NewRSAPrivateKey()
256 // ..and then use json.Unmarshal
257 // json.Unmarshal(key, jsonbuf)
258 //
259 // but if you don't know the type first, you have an abstract type
260 // jwk.Key, which can't be used as the first argument to json.Unmarshal
261 //
262 // In this case, use jwk.Parse()
263 fromJsonKey, err := jwk.Parse(jsonbuf)
264 if err != nil {
265 log.Printf("failed to parse json: %s", err)
266 return
267 }
268 _ = fromJsonKey
269 _ = fromRawKey
270 }
271}
272```
273
274
View as plain text