github.com/lestrrat-go/jwx/v2@v2.0.21/docs/02-jws.md (about)

     1  # Working with JWS
     2  
     3  In this document we describe how to work with JWS using [`github.com/lestrrat-go/jwx/v2/jws`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jws)
     4  
     5  * [Parsing](#parsing)
     6    * [Parse a JWS message stored in memory](#parse-a-jws-message-stored-in-memory)
     7    * [Parse a JWS message stored in a file](#parse-a-jws-message-stored-in-a-file)
     8    * [Parse a JWS message and access JWS headers](#parse-a-jws-message-and-access-jws-headers)
     9  * [Signing](#signing)
    10    * [Generating a JWS message in compact serialization format](#generating-a-jws-message-in-compact-serialization-format)
    11    * [Generating a JWS message in JSON serialization format](#generating-a-jws-message-in-json-serialization-format)
    12    * [Generating a JWS message with detached payload](#generating-a-jws-message-with-detached-payload)
    13    * [Using cloud KMS services](#using-cloud-kms-services)
    14    * [Including arbitrary headers](#including-arbitrary-headers)
    15  * [Verifying](#verifying)
    16    * [Verification using a single key](#verification-using-a-single-key)
    17    * [Verification using a JWKS](#verification-using-a-jwks)
    18    * [Verification using a detached payload](#verification-using-a-detached-payload)
    19    * [Verification using `jku`](#verification-using-jku)
    20  * [Using a custom signing/verification algorithm](#using-a-customg-signingverification-algorithm)
    21  * [Enabling ES256K](#enabling-es256k)
    22  
    23  # Parsing
    24  
    25  Parsing a JWS message means taking either a JWS message serialized in JSON or Compact form
    26  and loading it into a `jws.Message` object. No verification is performed, and therefore
    27  you cannot "trust" the contents in the same way that a verified message could be trusted.
    28  
    29  Also, be aware that a `jws.Message` is not meant to be used for either signing or
    30  verification. It is only provided such that it can be inspected -- there is no way
    31  to sign or verify using a parsed `jws.Message`. To do this, you would need to use
    32  `jws.Sign()` or `jws.Message()`.
    33  
    34  ## Parse a JWS message stored in memory
    35  
    36  You can parse a JWS message in memory stored as `[]byte` into a [`jws.Message`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jws#Message) object. In this mode, there is no verification performed.
    37  
    38  <!-- INCLUDE(examples/jws_parse_example_test.go) -->
    39  ```go
    40  package examples_test
    41  
    42  import (
    43    "encoding/json"
    44    "fmt"
    45    "os"
    46  
    47    "github.com/lestrrat-go/jwx/v2/jws"
    48  )
    49  
    50  func ExampleJWS_Parse() {
    51    const src = `eyJhbGciOiJIUzI1NiJ9.TG9yZW0gaXBzdW0.idbECxA8ZhQbU0ddZmzdRZxQmHjwvw77lT2bwqGgNMo`
    52  
    53    msg, err := jws.Parse([]byte(src))
    54    if err != nil {
    55      fmt.Printf("failed to parse JWS message: %s\n", err)
    56      return
    57    }
    58  
    59    json.NewEncoder(os.Stdout).Encode(msg)
    60    // OUTPUT:
    61    // {"payload":"TG9yZW0gaXBzdW0","protected":"eyJhbGciOiJIUzI1NiJ9","signature":"idbECxA8ZhQbU0ddZmzdRZxQmHjwvw77lT2bwqGgNMo"}
    62  }
    63  ```
    64  source: [examples/jws_parse_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jws_parse_example_test.go)
    65  <!-- END INCLUDE -->
    66  
    67  ## Parse a JWS message stored in a file
    68  
    69  To parse a JWS stored in a file, use [`jws.ReadFile()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jws#ReadFile). [`jws.ReadFile()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jws#ReadFile) accepts the same options as [`jws.Parse()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jws#Parse).
    70  
    71  <!-- INCLUDE(examples/jws_readfile_example_test.go) -->
    72  ```go
    73  package examples_test
    74  
    75  import (
    76    "encoding/json"
    77    "fmt"
    78    "os"
    79  
    80    "github.com/lestrrat-go/jwx/v2/jws"
    81  )
    82  
    83  func ExampleJWS_ReadFile() {
    84    const src = `eyJhbGciOiJIUzI1NiJ9.TG9yZW0gaXBzdW0.idbECxA8ZhQbU0ddZmzdRZxQmHjwvw77lT2bwqGgNMo`
    85    f, err := os.CreateTemp(``, `jws_readfile-*.jws`)
    86    if err != nil {
    87      fmt.Printf("failed to create temporary file: %s\n", err)
    88      return
    89    }
    90    defer os.Remove(f.Name())
    91  
    92    fmt.Fprintf(f, src)
    93    f.Close()
    94  
    95    msg, err := jws.ReadFile(f.Name())
    96    if err != nil {
    97      fmt.Printf("failed to parse JWS message: %s\n", err)
    98      return
    99    }
   100  
   101    json.NewEncoder(os.Stdout).Encode(msg)
   102  
   103    // OUTPUT:
   104    // {"payload":"TG9yZW0gaXBzdW0","protected":"eyJhbGciOiJIUzI1NiJ9","signature":"idbECxA8ZhQbU0ddZmzdRZxQmHjwvw77lT2bwqGgNMo"}
   105  }
   106  ```
   107  source: [examples/jws_readfile_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jws_readfile_example_test.go)
   108  <!-- END INCLUDE -->
   109  
   110  ## Parse a JWS message and access JWS headers
   111  
   112  Note: If you are considering using JWS header fields to decide on which key to use for verification, consider [using a `jwt.KeyProvider`](./01-jwt.md#parse-and-verify-a-jwt-using-arbitrary-keys).
   113  
   114  While a lot of documentation in the wild treat as if a JWT message encoded in base64 is... a JWT message, in truth it is a JWT message enveloped in a JWS message. Therefore in order to access the JWS headers of a JWT message you will need to work witha `jws.Message` object, which you can obtain from parsing the JWS payload. You will need to understand [the structure of a generic JWS message](https://www.rfc-editor.org/rfc/rfc7515#section-7.2.1).
   115  
   116  Below sample code extracts the `kid` field of a single-signature JWS message:
   117  
   118  <!-- INCLUDE(examples/jws_use_jws_header_test.go) -->
   119  ```go
   120  package examples_test
   121  
   122  import (
   123    "fmt"
   124  
   125    "github.com/lestrrat-go/jwx/v2/jwa"
   126    "github.com/lestrrat-go/jwx/v2/jwk"
   127    "github.com/lestrrat-go/jwx/v2/jws"
   128    "github.com/lestrrat-go/jwx/v2/jwt"
   129  )
   130  
   131  func ExampleJWS_UseJWSHeader() {
   132    key, err := jwk.FromRaw([]byte(`abracadabra`))
   133    if err != nil {
   134      fmt.Printf(`failed to create new symmetric key: %s`, err)
   135      return
   136    }
   137    key.Set(jws.KeyIDKey, `secret-key`)
   138  
   139    tok, err := jwt.NewBuilder().
   140      Issuer(`github.com/lestrrat-go/jwx`).
   141      Build()
   142    if err != nil {
   143      fmt.Printf(`failed to build token: %s`, err)
   144      return
   145    }
   146  
   147    signed, err := jwt.Sign(tok, jwt.WithKey(jwa.HS256, key))
   148    if err != nil {
   149      fmt.Printf(`failed to sign token: %s`, err)
   150      return
   151    }
   152  
   153    msg, err := jws.Parse(signed)
   154    if err != nil {
   155      fmt.Printf(`failed to parse serialized JWT: %s`, err)
   156      return
   157    }
   158  
   159    // While JWT enveloped with JWS in compact format only has 1 signature,
   160    // a generic JWS message may have multiple signatures. Therefore we
   161    // need to access the first element
   162    fmt.Printf("%q\n", msg.Signatures()[0].ProtectedHeaders().KeyID())
   163    // OUTPUT:
   164    // "secret-key"
   165  }
   166  ```
   167  source: [examples/jws_use_jws_header_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jws_use_jws_header_test.go)
   168  <!-- END INCLUDE -->
   169  
   170  # Signing
   171  
   172  ## Generating a JWS message in compact serialization format
   173  
   174  To sign an arbitrary payload as a JWS message in compact serialization format, use `jwt.Sign()`.
   175  
   176  Note that this would be [slightly different if you are signing JWTs](01-jwt.md#serialize-using-jws), as you would be
   177  using functions from the `jwt` package instead of `jws`.
   178  
   179  <!-- INCLUDE(examples/jws_sign_example_test.go) -->
   180  ```go
   181  package examples_test
   182  
   183  import (
   184    "fmt"
   185  
   186    "github.com/lestrrat-go/jwx/v2/jwa"
   187    "github.com/lestrrat-go/jwx/v2/jwk"
   188    "github.com/lestrrat-go/jwx/v2/jws"
   189  )
   190  
   191  func ExampleJWS_Sign() {
   192    key, err := jwk.FromRaw([]byte(`abracadabra`))
   193    if err != nil {
   194      fmt.Printf("failed to create key: %s\n", err)
   195      return
   196    }
   197  
   198    buf, err := jws.Sign([]byte("Lorem ipsum"), jws.WithKey(jwa.HS256, key))
   199    if err != nil {
   200      fmt.Printf("failed to sign payload: %s\n", err)
   201      return
   202    }
   203    fmt.Printf("%s\n", buf)
   204    // OUTPUT:
   205    // eyJhbGciOiJIUzI1NiJ9.TG9yZW0gaXBzdW0.EjVtju0uXjSz6QevNgAqN1ESd9aNCP7-tJLifkQ0_C0
   206  }
   207  ```
   208  source: [examples/jws_sign_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jws_sign_example_test.go)
   209  <!-- END INCLUDE -->
   210  
   211  ## Generating a JWS message in JSON serialization format
   212  
   213  Generally the only time you need to use a JSON serialization format is when you have to generate multiple signatures for a given payload using multiple signing algorithms and keys.
   214  
   215  When this need arises, use the [`jws.Sign()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jws#Sign) function with the `jws.WithJSON()` option and multiple `jws.WithKey()` options:
   216  
   217  <!-- INCLUDE(examples/jws_sign_json_example_test.go) -->
   218  ```go
   219  package examples_test
   220  
   221  import (
   222    "fmt"
   223  
   224    "github.com/lestrrat-go/jwx/v2/jwa"
   225    "github.com/lestrrat-go/jwx/v2/jwk"
   226    "github.com/lestrrat-go/jwx/v2/jws"
   227  )
   228  
   229  func ExampleJWS_SignJSON() {
   230    var keys []jwk.Key
   231  
   232    for i := 0; i < 3; i++ {
   233      key, err := jwk.FromRaw([]byte(fmt.Sprintf(`abracadabra-%d`, i)))
   234      if err != nil {
   235        fmt.Printf("failed to create key: %s\n", err)
   236        return
   237      }
   238      keys = append(keys, key)
   239    }
   240  
   241    options := []jws.SignOption{jws.WithJSON()}
   242    for _, key := range keys {
   243      options = append(options, jws.WithKey(jwa.HS256, key))
   244    }
   245  
   246    buf, err := jws.Sign([]byte("Lorem ipsum"), options...)
   247    if err != nil {
   248      fmt.Printf("failed to sign payload: %s\n", err)
   249      return
   250    }
   251    fmt.Printf("%s\n", buf)
   252    // OUTPUT:
   253    // {"payload":"TG9yZW0gaXBzdW0","signatures":[{"protected":"eyJhbGciOiJIUzI1NiJ9","signature":"bCQtU2y4PEnG78dUN-tXea8YEwhBAzLX7ZEYlRVtX_g"},{"protected":"eyJhbGciOiJIUzI1NiJ9","signature":"0ovW79M_bbaRDBrBLaNKN7rgJeXaSRAnu5rhAuRXBR4"},{"protected":"eyJhbGciOiJIUzI1NiJ9","signature":"ZkUzwlK5E6LFKsYEIyUvskOKLMDxE0MvvkvNrwINNWE"}]}
   254  }
   255  ```
   256  source: [examples/jws_sign_json_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jws_sign_json_example_test.go)
   257  <!-- END INCLUDE -->
   258  
   259  ## Generating a JWS message with detached payload
   260  
   261  JWS messages can be constructed with a detached payload. Use the `jws.WithDetachedPayload()` option to
   262  create a JWS message with the message detached from the result.
   263  
   264  <!-- INCLUDE(examples/jws_sign_detached_payload_example_test.go) -->
   265  ```go
   266  package examples_test
   267  
   268  import (
   269    "fmt"
   270  
   271    "github.com/lestrrat-go/jwx/v2/jwa"
   272    "github.com/lestrrat-go/jwx/v2/jwk"
   273    "github.com/lestrrat-go/jwx/v2/jws"
   274  )
   275  
   276  func ExampleJWS_SignDetachedPayload() {
   277    payload := `$.02`
   278  
   279    key, err := jwk.FromRaw([]byte(`abracadabra`))
   280    if err != nil {
   281      fmt.Printf("failed to create symmetric key: %s\n", err)
   282      return
   283    }
   284  
   285    serialized, err := jws.Sign(nil, jws.WithKey(jwa.HS256, key), jws.WithDetachedPayload([]byte(payload)))
   286    if err != nil {
   287      fmt.Printf("failed to sign payload: %s\n", err)
   288      return
   289    }
   290  
   291    fmt.Printf("%s\n", serialized)
   292    // OUTPUT:
   293    // eyJhbGciOiJIUzI1NiJ9..H14oXKwyvAsl0IbBLjw9tLxNIoYisuIyb_oDV4-30Vk
   294  }
   295  ```
   296  source: [examples/jws_sign_detached_payload_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jws_sign_detached_payload_example_test.go)
   297  <!-- END INCLUDE -->
   298  
   299  ## Including arbitrary headers
   300  
   301  By default, only some header fields are included in the result from `jws.Sign()`.
   302  If you want to include more header fields in the resulting JWS, you will have to provide them via the `jws.WithProtectedHeaders()` option.
   303  
   304  While `jws.WithPublicHeaders()` exists to keep API symmetric and complete, for most
   305  cases you only want to use `jws.WithProtectedHeaders()`
   306  
   307  <!-- INCLUDE(examples/jws_sign_with_headers_example_test.go) -->
   308  ```go
   309  package examples_test
   310  
   311  import (
   312    "fmt"
   313  
   314    "github.com/lestrrat-go/jwx/v2/jwa"
   315    "github.com/lestrrat-go/jwx/v2/jwk"
   316    "github.com/lestrrat-go/jwx/v2/jws"
   317  )
   318  
   319  func ExampleJWS_SignWithHeaders() {
   320    key, err := jwk.FromRaw([]byte(`abracadabra`))
   321    if err != nil {
   322      fmt.Printf("failed to create key: %s\n", err)
   323      return
   324    }
   325  
   326    hdrs := jws.NewHeaders()
   327    hdrs.Set(`x-example`, true)
   328    buf, err := jws.Sign([]byte("Lorem ipsum"), jws.WithKey(jwa.HS256, key, jws.WithProtectedHeaders(hdrs)))
   329    if err != nil {
   330      fmt.Printf("failed to sign payload: %s\n", err)
   331      return
   332    }
   333    fmt.Printf("%s\n", buf)
   334    // OUTPUT:
   335    // eyJhbGciOiJIUzI1NiIsIngtZXhhbXBsZSI6dHJ1ZX0.TG9yZW0gaXBzdW0.9nIX0hN7u1b97UcjmrVvd5y1ubkQp_1gz1V3Mkkcm14
   336  }
   337  ```
   338  source: [examples/jws_sign_with_headers_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jws_sign_with_headers_example_test.go)
   339  <!-- END INCLUDE -->
   340  
   341  ## Using cloud KMS services
   342  
   343  If you want to use cloud KMSes such as AWS KMS to sign and verify payloads, look for an object that implements
   344  `crypto.Signer`. There are some [implementations written for this module](https://github.com/jwx-go/crypto-signer).
   345  
   346  Event if you cannot find an implementation that you are looking for in the above repository, any other implementation that implements `crypto.Signer` should work.
   347  
   348  # Verifying
   349  
   350  ## Verification using a single key
   351  
   352  To verify a JWS message using a single key, use `jws.Verify()` with the `jws.WithKey()` option.
   353  It will automatically do the right thing whether it's serialized in compact form or JSON form.
   354  
   355  The `alg` must be explicitly specified. See "[Why don't you automatically infer the algorithm for `jws.Verify`?](99-faq.md#why-dont-you-automatically-infer-the-algorithm-for-jwsverify-)"
   356  
   357  <!-- INCLUDE(examples/jws_verify_with_key_example_test.go) -->
   358  ```go
   359  package examples_test
   360  
   361  import (
   362    "fmt"
   363  
   364    "github.com/lestrrat-go/jwx/v2/jwa"
   365    "github.com/lestrrat-go/jwx/v2/jwk"
   366    "github.com/lestrrat-go/jwx/v2/jws"
   367  )
   368  
   369  func ExampleJWS_VerifyWithKey() {
   370    const src = `eyJhbGciOiJIUzI1NiJ9.TG9yZW0gaXBzdW0.EjVtju0uXjSz6QevNgAqN1ESd9aNCP7-tJLifkQ0_C0`
   371  
   372    key, err := jwk.FromRaw([]byte(`abracadabra`))
   373    if err != nil {
   374      fmt.Printf("failed to create key: %s\n", err)
   375      return
   376    }
   377  
   378    buf, err := jws.Verify([]byte(src), jws.WithKey(jwa.HS256, key))
   379    if err != nil {
   380      fmt.Printf("failed to verify payload: %s\n", err)
   381      return
   382    }
   383    fmt.Printf("%s\n", buf)
   384    // OUTPUT:
   385    // Lorem ipsum
   386  }
   387  ```
   388  source: [examples/jws_verify_with_key_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jws_verify_with_key_example_test.go)
   389  <!-- END INCLUDE -->
   390  
   391  ## Verification using a JWKS
   392  
   393  To verify a payload using JWKS, by default you will need your payload and JWKS to have matching `kid` and `alg` fields.
   394  
   395  The `alg` field's requirement is the same for using a single key. See "[Why don't you automatically infer the algorithm for `jws.Verify`?](99-faq.md#why-dont-you-automatically-infer-the-algorithm-for-jwsverify-)"
   396  
   397  The `kid` field by default must match between the JWS signature and the key in JWKS. This can be explictly disabled by specifying `jws.WithRequireKid(false)` suboption when using the `jws.WithKeySet()` option (i.e.: `jws.WithKeySet(keyset, jws.WithRequireKid(false))`)
   398  
   399  For more discussion on why/how `alg`/`kid` values work, please read the [relevant section in the JWT documentation](01-jwt.md#parse-and-verify-a-jwt-with-a-key-set-matching-kid)
   400  
   401  <!-- INCLUDE(examples/jws_verify_with_keyset_example_test.go) -->
   402  ```go
   403  package examples_test
   404  
   405  import (
   406    "crypto/rand"
   407    "crypto/rsa"
   408    "fmt"
   409  
   410    "github.com/lestrrat-go/jwx/v2/jwa"
   411    "github.com/lestrrat-go/jwx/v2/jwk"
   412    "github.com/lestrrat-go/jwx/v2/jws"
   413  )
   414  
   415  func ExampleJWS_VerifyWithJWKSet() {
   416    // Setup payload first...
   417    privkey, err := rsa.GenerateKey(rand.Reader, 2048)
   418    if err != nil {
   419      fmt.Printf("failed to create private key: %s\n", err)
   420      return
   421    }
   422    const payload = "Lorem ipsum"
   423    signed, err := jws.Sign([]byte(payload), jws.WithKey(jwa.RS256, privkey))
   424    if err != nil {
   425      fmt.Printf("failed to sign payload: %s\n", err)
   426      return
   427    }
   428  
   429    // Create a JWK Set
   430    set := jwk.NewSet()
   431    // Add some bogus keys
   432    k1, _ := jwk.FromRaw([]byte("abracadabra"))
   433    set.AddKey(k1)
   434    k2, _ := jwk.FromRaw([]byte("opensesame"))
   435    set.AddKey(k2)
   436    // AddKey the real thing
   437    pubkey, _ := jwk.PublicRawKeyOf(privkey)
   438    k3, _ := jwk.FromRaw(pubkey)
   439    k3.Set(jwk.AlgorithmKey, jwa.RS256)
   440    set.AddKey(k3)
   441  
   442    // Up to this point, you probably will replace with a simple jwk.Fetch()
   443  
   444    // Now verify using the set.
   445    if _, err := jws.Verify(signed, jws.WithKeySet(set, jws.WithRequireKid(false))); err != nil {
   446      fmt.Printf("Failed to verify using jwk.Set: %s", err)
   447    }
   448  
   449    // OUTPUT:
   450  }
   451  ```
   452  source: [examples/jws_verify_with_keyset_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jws_verify_with_keyset_example_test.go)
   453  <!-- END INCLUDE -->
   454  
   455  ## Verification using a detached payload
   456  
   457  To verify a JWS message with detached payload, use the `jws.WithDetachedPayload()` option:
   458  
   459  <!-- INCLUDE(examples/jws_verify_detached_payload_example_test.go) -->
   460  ```go
   461  package examples_test
   462  
   463  import (
   464    "fmt"
   465  
   466    "github.com/lestrrat-go/jwx/v2/jwa"
   467    "github.com/lestrrat-go/jwx/v2/jwk"
   468    "github.com/lestrrat-go/jwx/v2/jws"
   469  )
   470  
   471  func ExampleJWS_VerifyDetachedPayload() {
   472    serialized := `eyJhbGciOiJIUzI1NiJ9..H14oXKwyvAsl0IbBLjw9tLxNIoYisuIyb_oDV4-30Vk`
   473    payload := `$.02`
   474  
   475    key, err := jwk.FromRaw([]byte(`abracadabra`))
   476    if err != nil {
   477      fmt.Printf("failed to create symmetric key: %s\n", err)
   478      return
   479    }
   480  
   481    verified, err := jws.Verify([]byte(serialized), jws.WithKey(jwa.HS256, key), jws.WithDetachedPayload([]byte(payload)))
   482    if err != nil {
   483      fmt.Printf("failed to verify payload: %s\n", err)
   484      return
   485    }
   486  
   487    fmt.Printf("%s\n", verified)
   488    // OUTPUT:
   489    // $.02
   490  }
   491  ```
   492  source: [examples/jws_verify_detached_payload_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jws_verify_detached_payload_example_test.go)
   493  <!-- END INCLUDE -->
   494  
   495  ## Verification using `jku`
   496  
   497  Regular calls to `jws.Verify()` does not respect the JWK Set referenced in the `jku` field. In order to
   498  verify the payload using the `jku` field, you must use the `jws.VerifyAuto()` function.
   499  
   500  ```go
   501  wl := ... // Create an appropriate whitelist
   502  payload, _ := jws.VerifyAuto(buf, jws.WithFetchWhitelist(wl))
   503  ```
   504  
   505  This will tell `jws` to verify the given buffer using the JWK Set presented at the URL specified in
   506  the `jku` field. If the buffer is a JSON message, then this is done for each of the signature in
   507  the `signatures` array.
   508  
   509  The URL in the `jku` field must have the `https` scheme, and the key ID in the JWK Set must
   510  match the key ID present in the JWS message.
   511  
   512  Because this operation will result in your program accessing remote resources, the default behavior
   513  is to NOT allow any URLs. You must specify a whitelist
   514  
   515  ```go
   516  wl := jwk.NewMapWhitelist().
   517    Add(`https://white-listed-address`)
   518  
   519  payload, _ := jws.VerifyAuto(buf, jws.WithFetchWhitelist(wl))
   520  ```
   521  
   522  If you want to allow any URLs to be accessible, use the `jwk.InsecureWhitelist`.
   523  
   524  ```go
   525  wl := jwk.InsecureWhitelist{}
   526  payload, _ := jws.VerifyAuto(buf, jws.WithFetchWhitelist(wl))
   527  ```
   528  
   529  If you must configure the HTTP Client in a special way, use the `jws.WithHTTPClient()` option:
   530  
   531  ```go
   532  client := &http.Client{ ... }
   533  payload, _ := jws.VerifyAuto(buf, jws.WithHTTPClient(client))
   534  ```
   535  
   536  # Using a custom signing/verification algorithm
   537  
   538  Sometimes we do not offer a particular algorithm out of the box, but you have an implementation for it.
   539  
   540  In such scenarios, you can use the [`jws.RegisterSigner()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jws#RegisterSigner) and [`jws.RegisterVerifier()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jws#RegisterVerifier) functions to
   541  generate your own verifier instance. 
   542  
   543  <!-- INCLUDE(examples/jws_custom_signer_verifier_example_test.go) -->
   544  ```go
   545  package examples_test
   546  
   547  import (
   548    "crypto/rand"
   549    "fmt"
   550  
   551    "github.com/cloudflare/circl/sign/ed25519"
   552    "github.com/lestrrat-go/jwx/v2/jwa"
   553    "github.com/lestrrat-go/jwx/v2/jws"
   554  )
   555  
   556  type CirclEdDSASignerVerifier struct{}
   557  
   558  func NewCirclEdDSASigner() (jws.Signer, error) {
   559    return &CirclEdDSASignerVerifier{}, nil
   560  }
   561  
   562  func NewCirclEdDSAVerifier() (jws.Verifier, error) {
   563    return &CirclEdDSASignerVerifier{}, nil
   564  }
   565  
   566  func (s CirclEdDSASignerVerifier) Algorithm() jwa.SignatureAlgorithm {
   567    return jwa.EdDSA
   568  }
   569  
   570  func (s CirclEdDSASignerVerifier) Sign(payload []byte, keyif interface{}) ([]byte, error) {
   571    switch key := keyif.(type) {
   572    case ed25519.PrivateKey:
   573      return ed25519.Sign(key, payload), nil
   574    default:
   575      return nil, fmt.Errorf(`invalid key type %T`, keyif)
   576    }
   577  }
   578  
   579  func (s CirclEdDSASignerVerifier) Verify(payload []byte, signature []byte, keyif interface{}) error {
   580    switch key := keyif.(type) {
   581    case ed25519.PublicKey:
   582      if ed25519.Verify(key, payload, signature) {
   583        return nil
   584      }
   585      return fmt.Errorf(`failed to verify EdDSA signature`)
   586    default:
   587      return fmt.Errorf(`invalid key type %T`, keyif)
   588    }
   589  }
   590  
   591  func ExampleJWS_CustomSignerVerifier() {
   592    // This example shows how to register external jws.Signer / jws.Verifier for
   593    // a given algorithm.
   594    jws.RegisterSigner(jwa.EdDSA, jws.SignerFactoryFn(NewCirclEdDSASigner))
   595    jws.RegisterVerifier(jwa.EdDSA, jws.VerifierFactoryFn(NewCirclEdDSAVerifier))
   596  
   597    pubkey, privkey, err := ed25519.GenerateKey(rand.Reader)
   598    if err != nil {
   599      fmt.Printf(`failed to generate keys: %s`, err)
   600      return
   601    }
   602  
   603    const payload = "Lorem Ipsum"
   604    signed, err := jws.Sign([]byte(payload), jws.WithKey(jwa.EdDSA, privkey))
   605    if err != nil {
   606      fmt.Printf(`failed to generate signed message: %s`, err)
   607      return
   608    }
   609  
   610    verified, err := jws.Verify(signed, jws.WithKey(jwa.EdDSA, pubkey))
   611    if err != nil {
   612      fmt.Printf(`failed to verify signed message: %s`, err)
   613      return
   614    }
   615  
   616    if string(verified) != payload {
   617      fmt.Printf(`got invalid payload: %s`, verified)
   618      return
   619    }
   620  
   621    // OUTPUT:
   622  }
   623  ```
   624  source: [examples/jws_custom_signer_verifier_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jws_custom_signer_verifier_example_test.go)
   625  <!-- END INCLUDE -->
   626  
   627  # Enabling ES256K
   628  
   629  See [Enabling Optional Signature Methods](./20-global-settings.md#enabling-optional-signature-methods)