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  ```