github.com/lestrrat-go/jwx/v2@v2.0.21/docs/04-jwk.md (about) 1 # Working with JWK 2 3 In this document we describe how to work with JWK using `github.com/lestrrat-go/jwx/v2/jwk` 4 5 * [Terminology](#terminology) 6 * [JWK / Key](#jwk--key) 7 * [JWK Set / Set](#jwk-set--set) 8 * [Raw Key](#raw-key) 9 * [Parsing](#parsing) 10 * [Parse a set](#parse-a-set) 11 * [Parse a key](#parse-a-key) 12 * [Parse a key or set in PEM format](#parse-a-key-or-a-set-in-pem-format) 13 * [Parse a key from a file](#parse-a-key-from-a-file) 14 * [Parse a key as a struct field](#parse-a-key-as-a-struct-field) 15 * [Construction](#construction) 16 * [Using jwk.FromRaw()](#using-jwkfromraw) 17 * [Construct a specific key type from scratch](#construct-a-specific-key-type-from-scratch) 18 * [Construct a specific key type from a raw key](#construct-a-specific-key-type-from-a-raw-key) 19 * [Fetching JWK Sets](#fetching-jwk-sets) 20 * [Parse a key from a remote resource](#parse-a-key-from-a-remote-resource) 21 * [Auto-refreshing remote keys](#auto-refreshing-remote-keys) 22 * [Using Whitelists](#using-whitelists) 23 * [Working with jwk.Key](#working-with-jwkkey) 24 * [Working with key-specific methods](#working-with-key-specific-methods) 25 * [Setting values to fields](#setting-values-to-fields) 26 * [Converting a jwk.Key to a raw key](#converting-a-jwkkey-to-a-raw-key) 27 28 --- 29 30 # Terminology 31 32 ## JWK / Key 33 34 Used to describe a JWK key, possibly of type RSA, ECDSA, OKP, or Symmetric. 35 36 ## JWK Set / Set 37 38 A "jwk" resource on the web can either contain a single JWK or an array of multiple JWKs. 39 The latter is called a JWK Set. 40 41 It is impossible to know what the resource contains beforehand, so functions like [`jwk.Parse()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#Parse) 42 and [`jwk.ReadFile()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#ReadFile) returns a [`jwk.Set`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#Set) by default. 43 44 ## Raw Key 45 46 Used to describe the underlying raw key that a JWK represents. For example, an RSA JWK can 47 represent rsa.PrivateKey/rsa.PublicKey, ECDSA JWK can represent ecdsa.PrivateKey/ecdsa.PublicKey, 48 and so forth 49 50 --- 51 52 The table below shows the matrix of key types and their respective `jwk.Key` and "raw" types. 53 If given anything else, `jwk.FromRaw` will return an error. 54 55 | | `jwk.Key` Type | Raw Key Type | 56 |-----------|----------------------------------------------|-------------------------------------------| 57 | RSA | `jwk.RSAPublicKey` / `jwk.RSAPrivateKey` | `*rsa.PublicKey` / `*rsa.PublicKey` | 58 | ECDSA | `jwk.ECDSAPublicKey` / `jwk.ECDSAPrivateKey` | `*ecdsa.PublicKey` / `*ecdsa.PublicKey` | 59 | OKP | `jwk.OKPPublicKey` / `jwk.OKPPrivateKey` | `ed25519.PublicKey` / `ed25519.PublicKey` | 60 | Symmetric | `jwk.SymmetricKey` | []byte | 61 62 # Parsing 63 64 ## Parse a set 65 66 If you have a key set, or are unsure if the source is a set or a single key, you should use [`jwk.Parse()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#Parse) 67 68 <!-- INCLUDE(examples/jwk_parse_jwks_example_test.go) --> 69 ```go 70 package examples_test 71 72 import ( 73 "encoding/json" 74 "fmt" 75 "os" 76 77 "github.com/lestrrat-go/jwx/v2/jwk" 78 ) 79 80 func ExampleJWK_ParseJWKS() { 81 const src = `{ 82 "keys": [ 83 {"kty":"EC", 84 "crv":"P-256", 85 "x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", 86 "y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", 87 "use":"enc", 88 "kid":"1"}, 89 {"kty":"RSA", 90 "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", 91 "e":"AQAB", 92 "alg":"RS256", 93 "kid":"2011-04-29"} 94 ] 95 }` 96 97 set, err := jwk.Parse([]byte(src)) 98 if err != nil { 99 fmt.Printf("failed to parse JWKS: %s\n", err) 100 return 101 } 102 103 json.NewEncoder(os.Stdout).Encode(set) 104 // OUTPUT: 105 // {"keys":[{"crv":"P-256","kid":"1","kty":"EC","use":"enc","x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4","y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"},{"alg":"RS256","e":"AQAB","kid":"2011-04-29","kty":"RSA","n":"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw"}]} 106 } 107 ``` 108 source: [examples/jwk_parse_jwks_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwk_parse_jwks_example_test.go) 109 <!-- END INCLUDE --> 110 111 ## Parse a key 112 113 If you are sure that the source only contains a single key, you can use [`jwk.ParseKey()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#ParseKey) 114 115 <!-- INCLUDE(examples/jwk_parse_key_example_test.go) --> 116 ```go 117 package examples_test 118 119 import ( 120 "encoding/json" 121 "fmt" 122 "os" 123 124 "github.com/lestrrat-go/jwx/v2/jwk" 125 ) 126 127 func ExampleJWK_ParseKey() { 128 const src = `{ 129 "kty":"EC", 130 "crv":"P-256", 131 "x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", 132 "y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", 133 "use":"enc", 134 "kid":"1" 135 }` 136 137 key, err := jwk.ParseKey([]byte(src)) 138 if err != nil { 139 fmt.Printf("failed parse key: %s\n", err) 140 return 141 } 142 143 json.NewEncoder(os.Stdout).Encode(key) 144 // OUTPUT: 145 // {"crv":"P-256","kid":"1","kty":"EC","use":"enc","x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4","y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"} 146 } 147 ``` 148 source: [examples/jwk_parse_key_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwk_parse_key_example_test.go) 149 <!-- END INCLUDE --> 150 151 ## Parse a key or a set in PEM format 152 153 Sometimes keys come in ASN.1 DER PEM format. To parse these files, use the [`jwk.WithPEM()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#WithPEM) option. 154 155 <!-- INCLUDE(examples/jwk_parse_with_pem_example_test.go) --> 156 ```go 157 package examples_test 158 159 import ( 160 "fmt" 161 "os" 162 163 "github.com/lestrrat-go/jwx/v2/internal/json" 164 "github.com/lestrrat-go/jwx/v2/jwk" 165 ) 166 167 func ExampleJWK_ParseWithPEM() { 168 const src = `-----BEGIN CERTIFICATE----- 169 MIIEljCCAn4CCQCTQBoGDvUbQTANBgkqhkiG9w0BAQsFADANMQswCQYDVQQGEwJK 170 UDAeFw0yMTA0MDEwMDE4MjhaFw0yMjA0MDEwMDE4MjhaMA0xCzAJBgNVBAYTAkpQ 171 MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvws4H/OxVS3CW1zvUgjs 172 H443df9zCAblLVPPdeRD11Jl1OZmGS7rtQNjQyT5xGpeuk77ZJcfDNLx+mSEtiYQ 173 V37GD5MPz+RX3hP2azuLvxoBseaHE6kC8tkDed8buQLl1hgms15KmKnt7E8B+EK2 174 1YRj0w6ZzehIllTbbj6gDJ39kZ2VHdLf5+4W0Kyh9cM4aA0si2jQJQsohW2rpt89 175 b+IagFau+sxP3GFUjSEvyXIamXhS0NLWuAW9UvY/RwhnIo5BzmWZd/y2R305T+QT 176 rHtb/8aGav8mP3uDx6AMDp/0UMKFUO4mpoOusMnrplUPS4Lz6RNpffmrrglOEuRZ 177 /eSFzGL35OeL12aYSyrbFIVsc/aLs6MkoplsuSG6Zhx345h/dA2a8Ub5khr6bksP 178 zGLer+bpBrQQsy21unvCIUz5y7uaYhV3Ql+aIZ+dwpEgZ3xxAvdKKeoCGQlhH/4J 179 0sSuutUtuTLfrBSgLHJEv2HIzeynChL2CYR8aku/nL68VTdmSt9UY2JGMOf9U8BI 180 fGRpkWBvI8hddMxNm8wF+09WScaZ2JWu7qW/l2jOdgesPIWRg+Hm3NaRSHqAWCOq 181 VUJk9WkCAye0FPALqSvH0ApDKxNtGZb5JZRCW19TqmhgXbAqIf5hsxDaGIXZcW9S 182 CqapZPw7Ccs7BOKSFvmM9p0CAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAVfLzKRdA 183 0vFpAAp3K+CDth7mag2WWFOXjlWZ+4pxfEBX3k7erJbj6+qYuCvCHXqIZnK1kZzD 184 p4zwsu8t8RfSmPvxcm/jkvecG4DAIGTdhBVtAf/9PU3e4kZFQCqizicQABh+ZFKV 185 dDtkRebUA5EAvP8E/OrvrjYU5xnOxOZU3arVXJfKFjVD619qLuF8XXW5700Gdqwn 186 wBgasTCCg9+tniiscKaET1m9C4PdrlXuAIscV9tGcJ7yEAao1BXokyJ+mK6K2Zv1 187 z/vvUJA/rGMBJoUjnWrRHON1JMNou2KyRO6z37GpRnfPiNgFpGv2x3ZNeix7H4bP 188 6+x4KZWQir5047p9hV4YrqMXeULEj3uG2GnOgdR7+hiN39arFVr11DMgABmx19SM 189 VQpTHrC8a605wwCBWnkiYdNojLa5WgeEHdBghKVpWnx9frYgZcz2UP861el5Lg9R 190 j04wkGL4IORYiM7VHSHNU4u/dlgfQE1y0T+1CzXwquy4csvbBzBKnZ1o9ZBsOtWS 191 ox0RaBsMD70mvTwKKmlCSD5HgZZTC0CfGWk4dQp/Mct5Z0x0HJMEJCJzpgTn3CRX 192 z8CjezfckLs7UKJOlhu3OU9TFsiGDzSDBZdDWO1/uciJ/AAWeSmsBt8cKL0MirIr 193 c4wOvhbalcX0FqTM3mXCgMFRbibquhwdxbU= 194 -----END CERTIFICATE-----` 195 196 key, err := jwk.ParseKey([]byte(src), jwk.WithPEM(true)) 197 if err != nil { 198 fmt.Printf("failed to parse key in PEM format: %s\n", err) 199 return 200 } 201 202 json.NewEncoder(os.Stdout).Encode(key) 203 // OUTPUT: 204 // {"e":"AQAB","kty":"RSA","n":"vws4H_OxVS3CW1zvUgjsH443df9zCAblLVPPdeRD11Jl1OZmGS7rtQNjQyT5xGpeuk77ZJcfDNLx-mSEtiYQV37GD5MPz-RX3hP2azuLvxoBseaHE6kC8tkDed8buQLl1hgms15KmKnt7E8B-EK21YRj0w6ZzehIllTbbj6gDJ39kZ2VHdLf5-4W0Kyh9cM4aA0si2jQJQsohW2rpt89b-IagFau-sxP3GFUjSEvyXIamXhS0NLWuAW9UvY_RwhnIo5BzmWZd_y2R305T-QTrHtb_8aGav8mP3uDx6AMDp_0UMKFUO4mpoOusMnrplUPS4Lz6RNpffmrrglOEuRZ_eSFzGL35OeL12aYSyrbFIVsc_aLs6MkoplsuSG6Zhx345h_dA2a8Ub5khr6bksPzGLer-bpBrQQsy21unvCIUz5y7uaYhV3Ql-aIZ-dwpEgZ3xxAvdKKeoCGQlhH_4J0sSuutUtuTLfrBSgLHJEv2HIzeynChL2CYR8aku_nL68VTdmSt9UY2JGMOf9U8BIfGRpkWBvI8hddMxNm8wF-09WScaZ2JWu7qW_l2jOdgesPIWRg-Hm3NaRSHqAWCOqVUJk9WkCAye0FPALqSvH0ApDKxNtGZb5JZRCW19TqmhgXbAqIf5hsxDaGIXZcW9SCqapZPw7Ccs7BOKSFvmM9p0"} 205 } 206 ``` 207 source: [examples/jwk_parse_with_pem_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwk_parse_with_pem_example_test.go) 208 <!-- END INCLUDE --> 209 210 ## Parse a key from a file 211 212 To parse keys stored in a file, [`jwk.ReadFile()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#ReadFile) can be used. 213 214 <!-- INCLUDE(examples/jwk_readfile_example_test.go) --> 215 ```go 216 package examples_test 217 218 import ( 219 "encoding/json" 220 "fmt" 221 "os" 222 223 "github.com/lestrrat-go/jwx/v2/jwk" 224 ) 225 226 func ExampleJWK_ReadFile() { 227 const src = `{ 228 "keys": [ 229 {"kty":"EC", 230 "crv":"P-256", 231 "x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", 232 "y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", 233 "use":"enc", 234 "kid":"1"}, 235 {"kty":"RSA", 236 "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", 237 "e":"AQAB", 238 "alg":"RS256", 239 "kid":"2011-04-29"} 240 ] 241 }` 242 243 f, err := os.CreateTemp(``, `jwk_readfile-*.jwk`) 244 if err != nil { 245 fmt.Printf("failed to create temporary file: %s\n", err) 246 return 247 } 248 defer os.Remove(f.Name()) 249 250 fmt.Fprintf(f, src) 251 f.Close() 252 253 key, err := jwk.ReadFile(f.Name()) 254 if err != nil { 255 fmt.Printf("failed to parse key: %s\n", err) 256 return 257 } 258 259 json.NewEncoder(os.Stdout).Encode(key) 260 261 // OUTPUT: 262 // {"keys":[{"crv":"P-256","kid":"1","kty":"EC","use":"enc","x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4","y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"},{"alg":"RS256","e":"AQAB","kid":"2011-04-29","kty":"RSA","n":"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw"}]} 263 } 264 ``` 265 source: [examples/jwk_readfile_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwk_readfile_example_test.go) 266 <!-- END INCLUDE --> 267 268 `jwk.ReadFile()` accepts the same options as [`jwk.Parse()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#Parse), therefore you can read a PEM-encoded file via the following incantation: 269 270 <!-- INCLUDE(examples/jwk_readfile_with_pem_example_test.go) --> 271 ```go 272 package examples_test 273 274 import ( 275 "fmt" 276 "os" 277 278 "github.com/lestrrat-go/jwx/v2/internal/json" 279 "github.com/lestrrat-go/jwx/v2/jwk" 280 ) 281 282 func ExampleJWK_ReadFileWithPEM() { 283 const src = `-----BEGIN CERTIFICATE----- 284 MIIEljCCAn4CCQCTQBoGDvUbQTANBgkqhkiG9w0BAQsFADANMQswCQYDVQQGEwJK 285 UDAeFw0yMTA0MDEwMDE4MjhaFw0yMjA0MDEwMDE4MjhaMA0xCzAJBgNVBAYTAkpQ 286 MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvws4H/OxVS3CW1zvUgjs 287 H443df9zCAblLVPPdeRD11Jl1OZmGS7rtQNjQyT5xGpeuk77ZJcfDNLx+mSEtiYQ 288 V37GD5MPz+RX3hP2azuLvxoBseaHE6kC8tkDed8buQLl1hgms15KmKnt7E8B+EK2 289 1YRj0w6ZzehIllTbbj6gDJ39kZ2VHdLf5+4W0Kyh9cM4aA0si2jQJQsohW2rpt89 290 b+IagFau+sxP3GFUjSEvyXIamXhS0NLWuAW9UvY/RwhnIo5BzmWZd/y2R305T+QT 291 rHtb/8aGav8mP3uDx6AMDp/0UMKFUO4mpoOusMnrplUPS4Lz6RNpffmrrglOEuRZ 292 /eSFzGL35OeL12aYSyrbFIVsc/aLs6MkoplsuSG6Zhx345h/dA2a8Ub5khr6bksP 293 zGLer+bpBrQQsy21unvCIUz5y7uaYhV3Ql+aIZ+dwpEgZ3xxAvdKKeoCGQlhH/4J 294 0sSuutUtuTLfrBSgLHJEv2HIzeynChL2CYR8aku/nL68VTdmSt9UY2JGMOf9U8BI 295 fGRpkWBvI8hddMxNm8wF+09WScaZ2JWu7qW/l2jOdgesPIWRg+Hm3NaRSHqAWCOq 296 VUJk9WkCAye0FPALqSvH0ApDKxNtGZb5JZRCW19TqmhgXbAqIf5hsxDaGIXZcW9S 297 CqapZPw7Ccs7BOKSFvmM9p0CAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAVfLzKRdA 298 0vFpAAp3K+CDth7mag2WWFOXjlWZ+4pxfEBX3k7erJbj6+qYuCvCHXqIZnK1kZzD 299 p4zwsu8t8RfSmPvxcm/jkvecG4DAIGTdhBVtAf/9PU3e4kZFQCqizicQABh+ZFKV 300 dDtkRebUA5EAvP8E/OrvrjYU5xnOxOZU3arVXJfKFjVD619qLuF8XXW5700Gdqwn 301 wBgasTCCg9+tniiscKaET1m9C4PdrlXuAIscV9tGcJ7yEAao1BXokyJ+mK6K2Zv1 302 z/vvUJA/rGMBJoUjnWrRHON1JMNou2KyRO6z37GpRnfPiNgFpGv2x3ZNeix7H4bP 303 6+x4KZWQir5047p9hV4YrqMXeULEj3uG2GnOgdR7+hiN39arFVr11DMgABmx19SM 304 VQpTHrC8a605wwCBWnkiYdNojLa5WgeEHdBghKVpWnx9frYgZcz2UP861el5Lg9R 305 j04wkGL4IORYiM7VHSHNU4u/dlgfQE1y0T+1CzXwquy4csvbBzBKnZ1o9ZBsOtWS 306 ox0RaBsMD70mvTwKKmlCSD5HgZZTC0CfGWk4dQp/Mct5Z0x0HJMEJCJzpgTn3CRX 307 z8CjezfckLs7UKJOlhu3OU9TFsiGDzSDBZdDWO1/uciJ/AAWeSmsBt8cKL0MirIr 308 c4wOvhbalcX0FqTM3mXCgMFRbibquhwdxbU= 309 -----END CERTIFICATE-----` 310 311 f, err := os.CreateTemp(``, `jwk_readfile_with_pem-*.jwk`) 312 if err != nil { 313 fmt.Printf("failed to create temporary file: %s\n", err) 314 return 315 } 316 defer os.Remove(f.Name()) 317 318 fmt.Fprintf(f, src) 319 f.Close() 320 321 key, err := jwk.ReadFile(f.Name(), jwk.WithPEM(true)) 322 if err != nil { 323 fmt.Printf("failed to parse key in PEM format: %s\n", err) 324 return 325 } 326 327 json.NewEncoder(os.Stdout).Encode(key) 328 // OUTPUT: 329 // {"keys":[{"e":"AQAB","kty":"RSA","n":"vws4H_OxVS3CW1zvUgjsH443df9zCAblLVPPdeRD11Jl1OZmGS7rtQNjQyT5xGpeuk77ZJcfDNLx-mSEtiYQV37GD5MPz-RX3hP2azuLvxoBseaHE6kC8tkDed8buQLl1hgms15KmKnt7E8B-EK21YRj0w6ZzehIllTbbj6gDJ39kZ2VHdLf5-4W0Kyh9cM4aA0si2jQJQsohW2rpt89b-IagFau-sxP3GFUjSEvyXIamXhS0NLWuAW9UvY_RwhnIo5BzmWZd_y2R305T-QTrHtb_8aGav8mP3uDx6AMDp_0UMKFUO4mpoOusMnrplUPS4Lz6RNpffmrrglOEuRZ_eSFzGL35OeL12aYSyrbFIVsc_aLs6MkoplsuSG6Zhx345h_dA2a8Ub5khr6bksPzGLer-bpBrQQsy21unvCIUz5y7uaYhV3Ql-aIZ-dwpEgZ3xxAvdKKeoCGQlhH_4J0sSuutUtuTLfrBSgLHJEv2HIzeynChL2CYR8aku_nL68VTdmSt9UY2JGMOf9U8BIfGRpkWBvI8hddMxNm8wF-09WScaZ2JWu7qW_l2jOdgesPIWRg-Hm3NaRSHqAWCOqVUJk9WkCAye0FPALqSvH0ApDKxNtGZb5JZRCW19TqmhgXbAqIf5hsxDaGIXZcW9SCqapZPw7Ccs7BOKSFvmM9p0"}]} 330 } 331 ``` 332 source: [examples/jwk_readfile_with_pem_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwk_readfile_with_pem_example_test.go) 333 <!-- END INCLUDE --> 334 335 ## Parse a key as a struct field 336 337 As `jwk.Key` is an interface, it can't directly be used as an argument in `json.Unmarsshal`. 338 For example, the following would fail: 339 340 ```go 341 var key jwk.Key 342 json.Unmarshal(data, &key) // error 343 ``` 344 345 This poses a problem when you want to use `jwk.Key` as a struct field in another struct 346 that needs to handle `json.Unmarshal`. To overcome this, you can either define a custom 347 `UnmarshalJSON([]byte) error` for your container struct, or you can use a "proxy" struct 348 that will intercept the field holding the `jwk.Key`. 349 350 <!-- INCLUDE(examples/jwk_struct_field_example_test.go) --> 351 ```go 352 package examples_test 353 354 import ( 355 "encoding/json" 356 "fmt" 357 "os" 358 359 "github.com/lestrrat-go/jwx/v2/jwk" 360 ) 361 362 type Container struct { 363 Key jwk.Key `json:"key"` 364 } 365 366 // This is only one way to parse a struct field whose dynamic 367 // type is unknown at compile time. In this example we use 368 // a proxy/wrapper to trick `Container` from attempting to 369 // parse the `.Key` field, and intercept the value that 370 // would have gone into the `Container` struct into 371 // `Proxy` struct's `.Key` struct field 372 type Proxy struct { 373 Container 374 Key json.RawMessage `json:"key"` 375 } 376 377 func ExampleJWK_StructField() { 378 const src = `{ 379 "key": { 380 "kty":"EC", 381 "crv":"P-256", 382 "x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", 383 "y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", 384 "use":"enc", 385 "kid":"1" 386 } 387 }` 388 389 var p Proxy 390 if err := json.Unmarshal([]byte(src), &p); err != nil { 391 fmt.Printf("failed to unmarshal from JSON: %s\n", err) 392 return 393 } 394 395 // Parse the intercepted `Proxy.Key` as a `jwk.Key` 396 // and assign it to `Container.Key` 397 key, err := jwk.ParseKey(p.Key) 398 if err != nil { 399 fmt.Printf("failed to parse key: %s\n", err) 400 return 401 } 402 p.Container.Key = key 403 404 json.NewEncoder(os.Stdout).Encode(p.Container) 405 // OUTPUT: 406 // {"key":{"crv":"P-256","kid":"1","kty":"EC","use":"enc","x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4","y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"}} 407 } 408 ``` 409 source: [examples/jwk_struct_field_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwk_struct_field_example_test.go) 410 <!-- END INCLUDE --> 411 412 # Construction 413 414 ## Using jwk.FromRaw() 415 416 Users can create a new key from scratch using [`jwk.FromRaw()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#FromRaw). 417 418 [`jwk.FromRaw()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#FromRaw) requires the raw key as its argument. 419 There are other ways to creating keys from a raw key, but they require knowing its type in advance. 420 Use [`jwk.FromRaw()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#FromRaw) when you have a key type which you do not know its underlying type in advance. 421 422 It automatically creates the appropriate underlying key based on the given argument type. 423 424 | Argument Type | Key Type | Note | 425 |---------------|----------|------| 426 | []byte | Symmetric Key | | 427 | ecdsa.PrivateKey | ECDSA Private Key | Argument may also be a pointer | 428 | ecdsa.PubliKey | ECDSA Public Key | Argument may also be a pointer | 429 | rsa.PrivateKey | RSA Private Key | Argument may also be a pointer | 430 | rsa.PubliKey | RSA Public Key | Argument may also be a pointer | 431 | x25519.PrivateKey | OKP Private Key | | 432 | x25519.PubliKey | OKP Public Key | | 433 434 <!-- INCLUDE(examples/jwk_from_raw_example_test.go) --> 435 ```go 436 package examples_test 437 438 import ( 439 "crypto/ecdsa" 440 "crypto/elliptic" 441 "crypto/rand" 442 "crypto/rsa" 443 "fmt" 444 445 "github.com/lestrrat-go/jwx/v2/jwk" 446 ) 447 448 func ExampleJWK_FromRaw() { 449 // First, THIS IS THE WRONG WAY TO USE jwk.FromRaw(). 450 // 451 // Assume that the file contains a JWK in JSON format 452 // 453 // buf, _ := os.ReadFile(file) 454 // key, _ := jwk.FromRaw(buf) 455 // 456 // This is not right, because the jwk.FromRaw() function determines 457 // the type of `jwk.Key` to create based on the TYPE of the argument. 458 // In this case the type of `buf` is always []byte, and therefore 459 // it will always create a symmetric key. 460 // 461 // What you want to do is to _parse_ `buf`. 462 // 463 // keyset, _ := jwk.Parse(buf) 464 // key, _ := jwk.ParseKey(buf) 465 // 466 // See other examples in examples/jwk_parse_key_example_test.go and 467 // examples/jwk_parse_jwks_example_test.go 468 469 // []byte -> jwk.SymmetricKey 470 { 471 raw := []byte("Lorem Ipsum") 472 key, err := jwk.FromRaw(raw) 473 if err != nil { 474 fmt.Printf("failed to create symmetric key: %s\n", err) 475 return 476 } 477 if _, ok := key.(jwk.SymmetricKey); !ok { 478 fmt.Printf("expected jwk.SymmetricKey, got %T\n", key) 479 return 480 } 481 } 482 483 // *rsa.PrivateKey -> jwk.RSAPrivateKey 484 // *rsa.PublicKey -> jwk.RSAPublicKey 485 { 486 raw, err := rsa.GenerateKey(rand.Reader, 2048) 487 if err != nil { 488 fmt.Printf("failed to generate new RSA private key: %s\n", err) 489 return 490 } 491 492 key, err := jwk.FromRaw(raw) 493 if err != nil { 494 fmt.Printf("failed to create symmetric key: %s\n", err) 495 return 496 } 497 if _, ok := key.(jwk.RSAPrivateKey); !ok { 498 fmt.Printf("expected jwk.SymmetricKey, got %T\n", key) 499 return 500 } 501 // PublicKey is omitted for brevity 502 } 503 504 // *ecdsa.PrivateKey -> jwk.ECDSAPrivateKey 505 // *ecdsa.PublicKey -> jwk.ECDSAPublicKey 506 { 507 raw, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) 508 if err != nil { 509 fmt.Printf("failed to generate new ECDSA private key: %s\n", err) 510 return 511 } 512 513 key, err := jwk.FromRaw(raw) 514 if err != nil { 515 fmt.Printf("failed to create symmetric key: %s\n", err) 516 return 517 } 518 if _, ok := key.(jwk.ECDSAPrivateKey); !ok { 519 fmt.Printf("expected jwk.SymmetricKey, got %T\n", key) 520 return 521 } 522 // PublicKey is omitted for brevity 523 } 524 525 // OUTPUT: 526 } 527 ``` 528 source: [examples/jwk_from_raw_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwk_from_raw_example_test.go) 529 <!-- END INCLUDE --> 530 531 # Fetching JWK Sets 532 533 ## Parse a key from a remote resource 534 535 To parse keys stored in a remote location pointed by a HTTP(s) URL, use [`jwk.Fetch()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#Fetch) 536 537 If you are going to be using this key repeatedly in a long running process, consider using [`jwk.Cache`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#Cache) or [`jwk.CachedSet`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#CachedSet) described elsewhere in this document. 538 539 <!-- INCLUDE(examples/jwk_fetch_example_test.go) --> 540 ```go 541 package examples_test 542 543 import ( 544 "context" 545 "encoding/json" 546 "fmt" 547 "net/http" 548 "net/http/httptest" 549 "os" 550 551 "github.com/lestrrat-go/jwx/v2/jwk" 552 ) 553 554 func ExampleJWK_Fetch() { 555 srv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 556 w.WriteHeader(http.StatusOK) 557 fmt.Fprintf(w, `{ 558 "keys": [ 559 {"kty":"EC", 560 "crv":"P-256", 561 "x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", 562 "y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", 563 "use":"enc", 564 "kid":"1"}, 565 {"kty":"RSA", 566 "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", 567 "e":"AQAB", 568 "alg":"RS256", 569 "kid":"2011-04-29"} 570 ] 571 }`) 572 })) 573 defer srv.Close() 574 575 set, err := jwk.Fetch( 576 context.Background(), 577 srv.URL, 578 // This is necessary because httptest.Server is using a custom certificate 579 jwk.WithHTTPClient(srv.Client()), 580 ) 581 if err != nil { 582 fmt.Printf("failed to fetch JWKS: %s\n", err) 583 return 584 } 585 586 json.NewEncoder(os.Stdout).Encode(set) 587 // OUTPUT: 588 // {"keys":[{"crv":"P-256","kid":"1","kty":"EC","use":"enc","x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4","y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"},{"alg":"RS256","e":"AQAB","kid":"2011-04-29","kty":"RSA","n":"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw"}]} 589 } 590 ``` 591 source: [examples/jwk_fetch_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwk_fetch_example_test.go) 592 <!-- END INCLUDE --> 593 594 ## Auto-refreshing remote keys 595 596 Sometimes you need to fetch a remote JWK, and use it mltiple times in a long-running process. 597 For example, you may act as an itermediary to some other service, and you may need to verify incoming JWT tokens against the tokens in said other service. 598 599 Normally, you should be able to simply fetch the JWK using [`jwk.Fetch()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#Fetch), 600 but keys are usually routinely expired and rotated due to security reasons. 601 In such cases you would need to refetch the JWK periodically, which is a pain. 602 603 `github.com/lestrrat-go/jwx/v2/jwk` provides the [`jwk.Cache`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#Cache) and [`jwk.CachedSet`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#CachedSet) to do this for you. 604 605 <!-- INCLUDE(examples/jwk_cache_example_test.go) --> 606 ```go 607 package examples_test 608 609 import ( 610 "context" 611 "fmt" 612 "time" 613 614 "github.com/lestrrat-go/jwx/v2/jwk" 615 ) 616 617 func ExampleJWK_Cache() { 618 ctx, cancel := context.WithCancel(context.Background()) 619 620 const googleCerts = `https://www.googleapis.com/oauth2/v3/certs` 621 622 // First, set up the `jwk.Cache` object. You need to pass it a 623 // `context.Context` object to control the lifecycle of the background fetching goroutine. 624 // 625 // Note that by default refreshes only happen very 15 minutes at the 626 // earliest. If you need to control this, use `jwk.WithRefreshWindow()` 627 c := jwk.NewCache(ctx) 628 629 // Tell *jwk.Cache that we only want to refresh this JWKS 630 // when it needs to (based on Cache-Control or Expires header from 631 // the HTTP response). If the calculated minimum refresh interval is less 632 // than 15 minutes, don't go refreshing any earlier than 15 minutes. 633 c.Register(googleCerts, jwk.WithMinRefreshInterval(15*time.Minute)) 634 635 // Refresh the JWKS once before getting into the main loop. 636 // This allows you to check if the JWKS is available before we start 637 // a long-running program 638 _, err := c.Refresh(ctx, googleCerts) 639 if err != nil { 640 fmt.Printf("failed to refresh google JWKS: %s\n", err) 641 return 642 } 643 644 // Pretend that this is your program's main loop 645 MAIN: 646 for { 647 select { 648 case <-ctx.Done(): 649 break MAIN 650 default: 651 } 652 keyset, err := c.Get(ctx, googleCerts) 653 if err != nil { 654 fmt.Printf("failed to fetch google JWKS: %s\n", err) 655 return 656 } 657 _ = keyset 658 // The returned `keyset` will always be "reasonably" new. 659 // 660 // By "reasonably" we mean that we cannot guarantee that the keys will be refreshed 661 // immediately after it has been rotated in the remote source. But it should be close\ 662 // enough, and should you need to forcefully refresh the token using the `(jwk.Cache).Refresh()` method. 663 // 664 // If re-fetching the keyset fails, a cached version will be returned from the previous successful 665 // fetch upon calling `(jwk.Cache).Fetch()`. 666 667 // Do interesting stuff with the keyset... but here, we just 668 // sleep for a bit 669 time.Sleep(time.Second) 670 671 // Because we're a dummy program, we just cancel the loop now. 672 // If this were a real program, you prosumably loop forever 673 cancel() 674 } 675 // OUTPUT: 676 } 677 ``` 678 source: [examples/jwk_cache_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwk_cache_example_test.go) 679 <!-- END INCLUDE --> 680 681 <!-- INCLUDE(examples/jwk_cached_set_example_test.go) --> 682 ```go 683 package examples_test 684 685 import ( 686 "context" 687 "fmt" 688 "time" 689 690 "github.com/lestrrat-go/jwx/v2/jwk" 691 "github.com/lestrrat-go/jwx/v2/jws" 692 ) 693 694 func ExampleJWK_CachedSet() { 695 ctx, cancel := context.WithCancel(context.Background()) 696 defer cancel() 697 698 const googleCerts = `https://www.googleapis.com/oauth2/v3/certs` 699 700 // The first steps are the same as examples/jwk_cache_example_test.go 701 c := jwk.NewCache(ctx) 702 c.Register(googleCerts, jwk.WithMinRefreshInterval(15*time.Minute)) 703 _, err := c.Refresh(ctx, googleCerts) 704 if err != nil { 705 fmt.Printf("failed to refresh google JWKS: %s\n", err) 706 return 707 } 708 709 cached := jwk.NewCachedSet(c, googleCerts) 710 711 // cached fulfills the jwk.Set interface. 712 var _ jwk.Set = cached 713 714 // That means you can pass it to things like jws.WithKeySet, 715 // allowing you to pretend as if you are using the result of 716 // 717 // jwk.Fetch(ctx, googleCerts) 718 // 719 // But you are instead using a cached (and periodically refreshed) 720 // for each operation. 721 _ = jws.WithKeySet(cached) 722 } 723 ``` 724 source: [examples/jwk_cached_set_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwk_cached_set_example_test.go) 725 <!-- END INCLUDE --> 726 727 ## Using Whitelists 728 729 If you are fetching JWK Sets from a possibly untrusted source such as the URL in the`"jku"` field of a JWS message, 730 you may have to perform some sort of whitelist checking. You can provide a `jwk.Whitelist` object to either 731 `jwk.Fetch()` or `(*jwk.Cache).Register()` methods to specify the use of a whitelist. 732 733 Currently the package provides `jwk.MapWhitelist` and `jwk.RegexpWhitelist` types for simpler cases, 734 as well as `jwk.InsecureWhitelist` for when you explicitly want to allo all URLs. 735 If you would like to implement something more complex, you can provide a function via `jwk.WhitelistFunc` or implement you own type of `jwk.Whitelist`. 736 737 <!-- INCLUDE(examples/jwk_whitelist_example_test.go) --> 738 ```go 739 package examples_test 740 741 import ( 742 "context" 743 "encoding/json" 744 "fmt" 745 "net/http" 746 "net/http/httptest" 747 "os" 748 "regexp" 749 750 "github.com/lestrrat-go/jwx/v2/jwk" 751 ) 752 753 func ExampleJWK_Whitelist() { 754 srv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 755 w.WriteHeader(http.StatusOK) 756 fmt.Fprintf(w, `{ 757 "keys": [ 758 {"kty":"EC", 759 "crv":"P-256", 760 "x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", 761 "y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", 762 "use":"enc", 763 "kid":"1"}, 764 {"kty":"RSA", 765 "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", 766 "e":"AQAB", 767 "alg":"RS256", 768 "kid":"2011-04-29"} 769 ] 770 }`) 771 })) 772 defer srv.Close() 773 774 testcases := []struct { 775 Whitelist jwk.Whitelist 776 Error bool 777 }{ 778 // The first two whitelists are meant to prevent access to any other 779 // URLs other than www.google.com 780 { 781 Whitelist: jwk.NewMapWhitelist().Add(`https://www.googleapis.com/oauth2/v3/certs`), 782 Error: true, 783 }, 784 { 785 Whitelist: jwk.NewRegexpWhitelist().Add(regexp.MustCompile(`^https://www\.googleapis\.com/`)), 786 Error: true, 787 }, 788 // This whitelist allows anything 789 { 790 Whitelist: jwk.InsecureWhitelist{}, 791 }, 792 } 793 794 for _, tc := range testcases { 795 set, err := jwk.Fetch( 796 context.Background(), 797 srv.URL, 798 // This is necessary because httptest.Server is using a custom certificate 799 jwk.WithHTTPClient(srv.Client()), 800 // Pass the whitelist! 801 jwk.WithFetchWhitelist(tc.Whitelist), 802 ) 803 if tc.Error { 804 if err == nil { 805 fmt.Printf("expected fetch to fail, but got no error\n") 806 return 807 } 808 } else { 809 if err != nil { 810 fmt.Printf("failed to fetch JWKS: %s\n", err) 811 return 812 } 813 json.NewEncoder(os.Stdout).Encode(set) 814 } 815 } 816 817 // OUTPUT: 818 // {"keys":[{"crv":"P-256","kid":"1","kty":"EC","use":"enc","x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4","y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"},{"alg":"RS256","e":"AQAB","kid":"2011-04-29","kty":"RSA","n":"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw"}]} 819 } 820 ``` 821 source: [examples/jwk_whitelist_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwk_whitelist_example_test.go) 822 <!-- END INCLUDE --> 823 824 # Working with jwk.Key 825 826 ## [Working with key-specific methods] 827 828 While you would almost always be able to get away with working with just the `jwk.Key` interface, there might be times when you want to get to methods that are specific to a particular key type, such as an RSA key. 829 830 In these cases it is possible to convert their types and get a more specific interface, such as `jwk.RSAPrivateKey` 831 832 <!-- INCLUDE(examples/jwk_key_specific_methods_example_test.go) --> 833 ```go 834 package examples_test 835 836 import ( 837 "crypto/rand" 838 "crypto/rsa" 839 "fmt" 840 841 "github.com/lestrrat-go/jwx/v2/jwk" 842 ) 843 844 func ExampleJWK_KeySpecificMethods() { 845 raw, err := rsa.GenerateKey(rand.Reader, 2048) 846 if err != nil { 847 fmt.Printf("failed to generate RSA private key: %s\n", err) 848 return 849 } 850 851 key, err := jwk.FromRaw(raw) 852 if err != nil { 853 fmt.Printf("failed to create jwk.Key from RSA private key: %s\n", err) 854 return 855 } 856 857 rsakey, ok := key.(jwk.RSAPrivateKey) 858 if !ok { 859 fmt.Printf("failed to convert jwk.Key into jwk.RSAPrivateKey (was %T)\n", key) 860 return 861 } 862 863 // We won't print these values, because each time they are 864 // generated the contents will be different, and thus our 865 // tests would fail. But here you can see that once you 866 // convert the type you can access the RSA-specific methods 867 _ = rsakey.D() 868 _ = rsakey.DP() 869 _ = rsakey.DQ() 870 _ = rsakey.E() 871 _ = rsakey.N() 872 _ = rsakey.P() 873 _ = rsakey.Q() 874 _ = rsakey.QI() 875 // OUTPUT: 876 // 877 } 878 ``` 879 source: [examples/jwk_key_specific_methods_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwk_key_specific_methods_example_test.go) 880 <!-- END INCLUDE --> 881 882 ## Setting values to fields 883 884 Using [`jwk.FromRaw()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#FromRaw) allows you to create a key whose fields have been properly populated, but sometimes there are other fields that you may want to populate in a key, such as`kid`, or other custom fields. 885 886 These fields can all be set using the [`jwk.Set()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#Set) method. 887 888 The [`jwk.Set()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#Set) method takes the name of the key, and a value to be associated with it. Some predefined keys have specific types (in which type checks are enforced), and others not. 889 890 [`jwk.Set()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#Set) may not alter the Key Type (`kty`) field of a key. 891 892 the `jwk` package defines field key names for predefined keys as constants so you won't ever have to bang your head againt the wall after finding out that you have a typo. 893 894 ```go 895 key.Set(jwk.KeyIDKey, `my-awesome-key`) 896 key.Set(`my-custom-field`, `unbelievable-value`) 897 ``` 898 899 ## Converting a jwk.Key to a raw key 900 901 As discussed in [Terminology](#terminology), this package calls the "original" keys (e.g. `rsa.PublicKey`, `ecdsa.PrivateKey`, etc) as "raw" keys. To obtain a raw key from a [`jwk.Key`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#Key) object, use the [`Raw()`](https://github.com/github.com/lestrrat-go/jwx/v2/jwk#Raw) method. 902 903 ```go 904 key, _ := jwk.ParseKey(src) 905 906 var raw interface{} 907 if err := key.Raw(&raw); err != nil { 908 ... 909 } 910 ``` 911 912 In the above example, `raw` contains whatever the [`jwk.Key`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#Key) represents. 913 If `key` represents an RSA key, it will contain either a `rsa.PublicKey` or `rsa.PrivateKey`. If it represents an ECDSA key, an `ecdsa.PublicKey`, or `ecdsa.PrivateKey`, etc. 914 915 If the only operation that you are performing is to grab the raw key out of a JSON JWK, use [`jwk.ParseRawKey`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#ParseRawKey) 916 917 ```go 918 var raw interface{} 919 if err := jwk.ParseRawKey(src, &raw); err != nil { 920 ... 921 } 922 ```