Repository: lestrrat-go/jwx
Pull request #633 (2022-03-09 05:51:05)

Deprecated jwk.New, tweak docs and tests

codecov-commenter: # [Codecov](https://codecov.io/gh/lestrrat-go/jwx/pull/633?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go) Report
> Merging [#633](https://codecov.io/gh/lestrrat-go/jwx/pull/633?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go) (eab9680) into [v2](https://codecov.io/gh/lestrrat-go/jwx/commit/759e542b0dc6e1342ec22f9f4752d18ba66fc90e?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go) (759e542) will **decrease** coverage by `0.24%`.
> The diff coverage is `95.83%`.

[![Impacted file tree graph](https://codecov.io/gh/lestrrat-go/jwx/pull/633/graphs/tree.svg?width=650&height=150&src=pr&token=kU2RSDosPt&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go)](https://codecov.io/gh/lestrrat-go/jwx/pull/633?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go)

```diff
@@            Coverage Diff             @@
##               v2     #633      +/-   ##
==========================================
- Coverage   72.36%   72.12%   -0.25%     
==========================================
  Files          88       87       -1     
  Lines        9775     9758      -17     
==========================================
- Hits         7074     7038      -36     
- Misses       1896     1913      +17     
- Partials      805      807       +2     
```


| [Impacted Files](https://codecov.io/gh/lestrrat-go/jwx/pull/633?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go) | Coverage Δ | |
|---|---|---|
| [jwk/ecdsa\_gen.go](https://codecov.io/gh/lestrrat-go/jwx/pull/633/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go#diff-andrL2VjZHNhX2dlbi5nbw==) | `72.32% <ø> (-0.35%)` | :arrow_down: |
| [jwk/okp\_gen.go](https://codecov.io/gh/lestrrat-go/jwx/pull/633/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go#diff-andrL29rcF9nZW4uZ28=) | `71.04% <ø> (-0.09%)` | :arrow_down: |
| [jwk/rsa\_gen.go](https://codecov.io/gh/lestrrat-go/jwx/pull/633/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go#diff-andrL3JzYV9nZW4uZ28=) | `71.66% <ø> (-0.33%)` | :arrow_down: |
| [jwk/symmetric\_gen.go](https://codecov.io/gh/lestrrat-go/jwx/pull/633/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go#diff-andrL3N5bW1ldHJpY19nZW4uZ28=) | `78.12% <ø> (-0.38%)` | :arrow_down: |
| [jwk/jwk.go](https://codecov.io/gh/lestrrat-go/jwx/pull/633/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go#diff-andrL2p3ay5nbw==) | `60.96% <94.73%> (-0.81%)` | :arrow_down: |
| [jwe/internal/keygen/keygen.go](https://codecov.io/gh/lestrrat-go/jwx/pull/633/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go#diff-andlL2ludGVybmFsL2tleWdlbi9rZXlnZW4uZ28=) | `62.65% <100.00%> (ø)` | |
| [jwk/ecdsa.go](https://codecov.io/gh/lestrrat-go/jwx/pull/633/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go#diff-andrL2VjZHNhLmdv) | `85.82% <100.00%> (ø)` | |
| [jwk/okp.go](https://codecov.io/gh/lestrrat-go/jwx/pull/633/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go#diff-andrL29rcC5nbw==) | `72.44% <100.00%> (ø)` | |
| [jwk/rsa.go](https://codecov.io/gh/lestrrat-go/jwx/pull/633/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go#diff-andrL3JzYS5nbw==) | `91.80% <100.00%> (ø)` | |
| [jwk/symmetric.go](https://codecov.io/gh/lestrrat-go/jwx/pull/633/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go#diff-andrL3N5bW1ldHJpYy5nbw==) | `73.91% <100.00%> (ø)` | |
| ... and [8 more](https://codecov.io/gh/lestrrat-go/jwx/pull/633/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/lestrrat-go/jwx/pull/633?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/lestrrat-go/jwx/pull/633?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go). Last update [759e542...eab9680](https://codecov.io/gh/lestrrat-go/jwx/pull/633?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go).


Patch:
diff --git a/Changes.v2 b/Changes.v2
index a13604fc..7a20412f 100644
--- a/Changes.v2
+++ b/Changes.v2
@@ -79,6 +79,16 @@ Changes
     by using `jwe.WithKeyProvider()`. Because this was the only consumer for
     `jwe.DecryptCtx`, this type has been removed as well.
 
+  * [jwk] `jwk.New()` has been renamed to `jwk.FromRaw()`, which hopefully will
+    make it easier for the users what the input should be.
+
+  * [jwk] `jwk.NewRSAPRivateKey()`, `jwk.NewECDSAPrivateKey()`, etc have been removed.
+    There is no longer any way to create concrete types of `jwk.Key` 
+
+  * [jwk] `jwk.Key` type no longer supports direct unmarshaling via `json.Unmarshal()`,
+    because you can no longer instantiate concrete `jwk.Key` types. You will need to
+    use `jwk.ParseKey()`. See the documentation for ways to parse JWKs.
+
   * [jwk] `(jwk.Key).Algorithm()` is now of `jwk.KeyAlgorithm` type. This field used
     to be `string` and therefore could not be passed directly to `jwt.Sign()`
     `jws.Sign()`, `jwe.Encrypt()`, et al. This is no longer the case, and
@@ -86,6 +96,9 @@ Changes
     https://github.com/lestrrat-go/jwx/blob/v2/docs/99-faq.md#why-is-jwkkeyalgorithm-and-jwakeyalgorithm-so-confusing
     for more details
 
+  * [jwk] jwk.SetFetcher and jwk.SetFetchFunc has been added.
+    They represent something that can fetch a jwk.Set
+
   * [jws] `jws.Sign()` can now generate JWS messages in either compact or JSON
     forms. By default, the compact form is used. JSON format can be
     enabled by using the `jws.WithJSON` option.
@@ -97,9 +110,6 @@ Changes
   * [jws] `jws.WithCompact` and `jws.WithJSON` options have been added
     to control the serialization format.
 
-  * [jwk] jwk.SetFetcher and jwk.SetFetchFunc has been added.
-    They represent something that can fetch a jwk.Set
-
   * [jws] jws.Verify()'s method signature has been changed to
 
       jwt.Verify([]byte, ...jws.VerifyOption) ([]byte, error)
diff --git a/cmd/jwx/jwk.go b/cmd/jwx/jwk.go
index da0507d2..4371b972 100644
--- a/cmd/jwx/jwk.go
+++ b/cmd/jwx/jwk.go
@@ -191,7 +191,7 @@ func makeJwkGenerateCmd() *cli.Command {
 				return fmt.Errorf(`failed to unmarshal template: %w`, err)
 			}
 		}
-		key, err := jwk.New(rawkey)
+		key, err := jwk.FromRaw(rawkey)
 		if err != nil {
 			return fmt.Errorf(`failed to create new JWK from raw key: %w`, err)
 		}
diff --git a/docs/04-jwk.md b/docs/04-jwk.md
index 8e13e3dd..ffa90467 100644
--- a/docs/04-jwk.md
+++ b/docs/04-jwk.md
@@ -11,17 +11,18 @@ In this document we describe how to work with JWK using `github.com/lestrrat-go/
   * [Parse a key](#parse-a-key)
   * [Parse a key or set in PEM format](#parse-a-key-or-a-set-in-pem-format)
   * [Parse a key from a file](#parse-a-key-from-a-file)
-  * [Parse a key from a remote resource](#parse-a-key-from-a-remote-resource)
+  * [Parse a key as a struct field](#parse-a-key-as-a-struct-field)
 * [Construction](#construction)
-  * [Using jwk.New()](#using-jwknew)
+  * [Using jwk.FromRaw()](#using-jwkfromraw)
   * [Construct a specific key type from scratch](#construct-a-specific-key-type-from-scratch)
   * [Construct a specific key type from a raw key](#construct-a-specific-key-type-from-a-raw-key)
-* [Setting values to fields](#setting-values-to-fields)
 * [Fetching JWK Sets](#fetching-jwk-sets)
-  * [Fetching a JWK Set once](#fetching-a-jwk-set-once)
+  * [Parse a key from a remote resource](#parse-a-key-from-a-remote-resource)
   * [Auto-refreshing remote keys](#auto-refreshing-remote-keys)
   * [Using Whitelists](#using-whitelists)
-* [Converting a jwk.Key to a raw key](#converting-a-jwkkey-to-a-raw-key)
+* [Working with jwk.Key](#working-with-jwkkey)
+  * [Setting values to fields](#setting-values-to-fields)
+  * [Converting a jwk.Key to a raw key](#converting-a-jwkkey-to-a-raw-key)
 
 ---
 
@@ -320,76 +321,33 @@ c4wOvhbalcX0FqTM3mXCgMFRbibquhwdxbU=
 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)
 <!-- END INCLUDE -->
 
-## Parse a key from a remote resource
+## Parse a key as a struct field
 
-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)
+As `jwk.Key` is an interface, it can't directly be used as an argument in `json.Unmarsshal`.
+For example, the following would fail:
 
-If you are going to be using this key repeatedly in a long running process, consider using [`jwk.AutoRefresh`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#AutoRefresh) described elsewhere in this document.
-
-<!-- INCLUDE(examples/jwk_fetch_example_test.go) -->
 ```go
-package examples
-
-import (
-  "context"
-  "encoding/json"
-  "fmt"
-  "net/http"
-  "net/http/httptest"
-  "os"
-
-  "github.com/lestrrat-go/jwx/v2/jwk"
-)
-
-func ExampleJWK_Fetch() {
-  srv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-    w.WriteHeader(http.StatusOK)
-    fmt.Fprintf(w, `{
-  		"keys": [
-        {"kty":"EC",
-         "crv":"P-256",
-         "x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
-         "y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
-         "use":"enc",
-         "kid":"1"},
-        {"kty":"RSA",
-         "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
-         "e":"AQAB",
-         "alg":"RS256",
-         "kid":"2011-04-29"}
-      ]
-    }`)
-  }))
-  defer srv.Close()
+var key jwk.Key
+json.Unmarshal(data, &key) // error
+```
 
-  set, err := jwk.Fetch(
-    context.Background(),
-    srv.URL,
-    // This is necessary because httptest.Server is using a custom certificate
-    jwk.WithHTTPClient(srv.Client()),
-  )
-  if err != nil {
-    fmt.Printf("failed to fetch JWKS: %s\n", err)
-    return
-  }
+This poses a problem when you want to use `jwk.Key` as a struct field in another struct
+that needs to handle `json.Unmarshal`. To overcome this, you can either define a custom
+`UnmarshalJSON([]byte) error` for your container struct, or you can use a "proxy" struct
+that will intercept the field holding the `jwk.Key`.
 
-  json.NewEncoder(os.Stdout).Encode(set)
-  // OUTPUT:
-  // {"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"}]}
-}
-```
-source: [examples/jwk_fetch_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwk_fetch_example_test.go)
+<!-- INCLUDE(examples/jwk_struct_field_example_test.go) -->
 <!-- END INCLUDE -->
 
 # Construction
 
-## Using jwk.New()
+## Using jwk.FromRaw()
 
-Users can create a new key from scratch using [`jwk.New()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#New).
+Users can create a new key from scratch using [`jwk.FromRaw()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#FromRaw).
 
-[`jwk.New()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#New) requires the raw key as its argument.
+[`jwk.FromRaw()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#FromRaw) requires the raw key as its argument.
 There are other ways to creating keys from a raw key, but they require knowing its type in advance.
-Use [`jwk.New()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#New) when you have a key type which you do not know its underlying type in advance.
+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.
 
 It automatically creates the appropriate underlying key based on the given argument type.
 
@@ -403,7 +361,7 @@ It automatically creates the appropriate underlying key based on the given argum
 | x25519.PrivateKey | OKP Private Key | |
 | x25519.PubliKey | OKP Public Key | |
 
-<!-- INCLUDE(examples/jwk_new_example_test.go) -->
+<!-- INCLUDE(examples/jwk_from_raw_example_test.go) -->
 ```go
 package examples
 
@@ -542,29 +500,68 @@ key := jwk.NewRSAPrivateKey()
 err := key.FromRaw(privkey)
 ```
 
-## Setting values to fields
-
-Using [`jwk.New()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#New) or [`jwk.FromRaw()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#FromRaw) allows you to populate the fields that are required to do perform the computations, but there are other fields that you may want to populate in a key. These fields can all be set using the [`jwk.Set()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#Set) method.
+# Fetching JWK Sets
 
-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.
+## Parse a key from a remote resource
 
-[`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.
+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)
 
-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.
+If you are going to be using this key repeatedly in a long running process, consider using [`jwk.AutoRefresh`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#AutoRefresh) described elsewhere in this document.
 
+<!-- INCLUDE(examples/jwk_fetch_example_test.go) -->
 ```go
-key.Set(jwk.KeyIDKey, `my-awesome-key`)
-key.Set(`my-custom-field`, `unbelievable-value`)
-```
-# Fetching JWK Sets
+package examples
 
-## Fetching a JWK Set once
+import (
+  "context"
+  "encoding/json"
+  "fmt"
+  "net/http"
+  "net/http/httptest"
+  "os"
 
-To fetch a JWK Set once, use `jwk.Fetch()`.
+  "github.com/lestrrat-go/jwx/v2/jwk"
+)
 
-```go
-set, err := jwk.Fetch(ctx, url, options...)
+func ExampleJWK_Fetch() {
+  srv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+    w.WriteHeader(http.StatusOK)
+    fmt.Fprintf(w, `{
+  		"keys": [
+        {"kty":"EC",
+         "crv":"P-256",
+         "x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
+         "y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
+         "use":"enc",
+         "kid":"1"},
+        {"kty":"RSA",
+         "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
+         "e":"AQAB",
+         "alg":"RS256",
+         "kid":"2011-04-29"}
+      ]
+    }`)
+  }))
+  defer srv.Close()
+
+  set, err := jwk.Fetch(
+    context.Background(),
+    srv.URL,
+    // This is necessary because httptest.Server is using a custom certificate
+    jwk.WithHTTPClient(srv.Client()),
+  )
+  if err != nil {
+    fmt.Printf("failed to fetch JWKS: %s\n", err)
+    return
+  }
+
+  json.NewEncoder(os.Stdout).Encode(set)
+  // OUTPUT:
+  // {"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"}]}
+}
 ```
+source: [examples/jwk_fetch_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwk_fetch_example_test.go)
+<!-- END INCLUDE -->
 
 ## Auto-refreshing remote keys
 
@@ -748,7 +745,26 @@ func ExampleJWK_Whitelist() {
 source: [examples/jwk_whitelist_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwk_whitelist_example_test.go)
 <!-- END INCLUDE -->
 
-# Converting a jwk.Key to a raw key
+# Working with jwk.Key
+
+## Setting values to fields
+
+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.
+
+These fields can all be set using the [`jwk.Set()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#Set) method.
+
+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.
+
+[`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.
+
+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.
+
+```go
+key.Set(jwk.KeyIDKey, `my-awesome-key`)
+key.Set(`my-custom-field`, `unbelievable-value`)
+```
+
+## Converting a jwk.Key to a raw key
 
 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.
 
diff --git a/go.sum b/go.sum
index 8b65fd13..9b588c89 100644
--- a/go.sum
+++ b/go.sum
@@ -5,6 +5,8 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d h1:
 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE=
 github.com/goccy/go-json v0.9.5 h1:ooSMW526ZjK+EaL5elrSyN2EzIfi/3V0m4+HJEDYLik=
 github.com/goccy/go-json v0.9.5/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/lestrrat-go/backoff v1.0.1 h1:Gphaach0QvvtaHmR9U8hwXNHXWckPyD8V6S+V+D184c=
+github.com/lestrrat-go/backoff v1.0.1/go.mod h1:5QVJKC49Q5yQvCrpup0ZqzDGHEO/O4H82cnDuYdumkw=
 github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A=
 github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=
 github.com/lestrrat-go/blackmagic v1.0.0 h1:XzdxDbuQTz0RZZEmdU7cnQxUtFUzgCSPq8RCz4BxIi4=
diff --git a/internal/cmd/genoptions/main.go b/internal/cmd/genoptions/main.go
index 75f6a8a1..aab1d0f8 100644
--- a/internal/cmd/genoptions/main.go
+++ b/internal/cmd/genoptions/main.go
@@ -41,7 +41,8 @@ func writeComment(o *codegen.Output, comment string) bool {
 
 type Objects struct {
 	Output      string
-	PackageName string `yaml:"package_name"`
+	PackageName string   `yaml:"package_name"`
+	Imports     []string `yaml:"imports"`
 	Interfaces  []*struct {
 		Name         string
 		Comment      string
@@ -119,15 +120,16 @@ func genOptions(objects *Objects) error {
 
 	o.LL(`package %s`, objects.PackageName)
 
-	// Write all imports -- they will be pruned by golang.org/x/tools/imports eventually,
-	// so it's okay to be redundant
-	o.WriteImports(
+	imports := append(objects.Imports, []string{
 		`github.com/lestrrat-go/jwx/v2/jwa`,
 		`github.com/lestrrat-go/jwx/v2/jwe`,
 		`github.com/lestrrat-go/jwx/v2/jwk`,
 		`github.com/lestrrat-go/jwx/v2/jws`,
 		`github.com/lestrrat-go/jwx/v2/jwt`,
-	)
+	}...)
+	// Write all imports -- they will be pruned by golang.org/x/tools/imports eventually,
+	// so it's okay to be redundant
+	o.WriteImports(imports...)
 
 	o.LL(`type Option = option.Interface`)
 
diff --git a/jwe/internal/keygen/keygen.go b/jwe/internal/keygen/keygen.go
index a2d2c586..5b2d8a6a 100644
--- a/jwe/internal/keygen/keygen.go
+++ b/jwe/internal/keygen/keygen.go
@@ -152,7 +152,7 @@ func (g X25519) Generate() (ByteSource, error) {
 // HeaderPopulate populates the header with the required EC-DSA public key
 // information ('epk' key)
 func (k ByteWithECPublicKey) Populate(h Setter) error {
-	key, err := jwk.New(k.PublicKey)
+	key, err := jwk.FromRaw(k.PublicKey)
 	if err != nil {
 		return fmt.Errorf(`failed to create JWK: %w`, err)
 	}
diff --git a/jwk/ecdsa.go b/jwk/ecdsa.go
index 95612290..26bd02b9 100644
--- a/jwk/ecdsa.go
+++ b/jwk/ecdsa.go
@@ -140,7 +140,7 @@ func (k *ecdsaPrivateKey) Raw(v interface{}) error {
 func makeECDSAPublicKey(v interface {
 	makePairs() []*HeaderPair
 }) (Key, error) {
-	newKey := NewECDSAPublicKey()
+	newKey := newECDSAPublicKey()
 
 	// Iterate and copy everything except for the bits that should not be in the public key
 	for _, pair := range v.makePairs() {
diff --git a/jwk/ecdsa_gen.go b/jwk/ecdsa_gen.go
index c09c4925..ce74f641 100644
--- a/jwk/ecdsa_gen.go
+++ b/jwk/ecdsa_gen.go
@@ -51,10 +51,6 @@ type ecdsaPublicKey struct {
 	dc                     json.DecodeCtx
 }
 
-func NewECDSAPublicKey() ECDSAPublicKey {
-	return newECDSAPublicKey()
-}
-
 func newECDSAPublicKey() *ecdsaPublicKey {
 	return &ecdsaPublicKey{
 		mu:            &sync.RWMutex{},
@@ -618,10 +614,6 @@ type ecdsaPrivateKey struct {
 	dc                     json.DecodeCtx
 }
 
-func NewECDSAPrivateKey() ECDSAPrivateKey {
-	return newECDSAPrivateKey()
-}
-
 func newECDSAPrivateKey() *ecdsaPrivateKey {
 	return &ecdsaPrivateKey{
 		mu:            &sync.RWMutex{},
diff --git a/jwk/internal/cmd/genheader/main.go b/jwk/internal/cmd/genheader/main.go
index a6882b66..2ce510ef 100644
--- a/jwk/internal/cmd/genheader/main.go
+++ b/jwk/internal/cmd/genheader/main.go
@@ -287,9 +287,6 @@ func generateObject(o *codegen.Output, kt *KeyType, obj *codegen.Object) error {
 	o.L("dc json.DecodeCtx")
 	o.L("}")
 
-	o.LL("func New%[1]s() %[1]s {", ifName)
-	o.L("return new%s()", ifName)
-	o.L("}")
 	o.LL("func new%s() *%s {", ifName, structName)
 	o.L("return &%s{", structName)
 	o.L("mu: &sync.RWMutex{},")
diff --git a/jwk/jwk.go b/jwk/jwk.go
index c8eb65dd..4bcf1fbc 100644
--- a/jwk/jwk.go
+++ b/jwk/jwk.go
@@ -36,7 +36,7 @@ func bigIntToBytes(n *big.Int) ([]byte, error) {
 	return n.Bytes(), nil
 }
 
-// New creates a jwk.Key from the given key (RSA/ECDSA/symmetric keys).
+// FromRaw creates a jwk.Key from the given key (RSA/ECDSA/symmetric keys).
 //
 // The constructor auto-detects the type of key to be instantiated
 // based on the input type:
@@ -45,7 +45,7 @@ func bigIntToBytes(n *big.Int) ([]byte, error) {
 //   * "crypto/ecdsa".PrivateKey and "crypto/ecdsa".PublicKey creates an EC based key
 //   * "crypto/ed25519".PrivateKey and "crypto/ed25519".PublicKey creates an OKP based key
 //   * []byte creates a symmetric key
-func New(key interface{}) (Key, error) {
+func FromRaw(key interface{}) (Key, error) {
 	if key == nil {
 		return nil, fmt.Errorf(`jwk.New requires a non-nil key`)
 	}
@@ -66,55 +66,55 @@ func New(key interface{}) (Key, error) {
 
 	switch rawKey := ptr.(type) {
 	case *rsa.PrivateKey:
-		k := NewRSAPrivateKey()
+		k := newRSAPrivateKey()
 		if err := k.FromRaw(rawKey); err != nil {
 			return nil, fmt.Errorf(`failed to initialize %T from %T: %w`, k, rawKey, err)
 		}
 		return k, nil
 	case *rsa.PublicKey:
-		k := NewRSAPublicKey()
+		k := newRSAPublicKey()
 		if err := k.FromRaw(rawKey); err != nil {
 			return nil, fmt.Errorf(`failed to initialize %T from %T: %w`, k, rawKey, err)
 		}
 		return k, nil
 	case *ecdsa.PrivateKey:
-		k := NewECDSAPrivateKey()
+		k := newECDSAPrivateKey()
 		if err := k.FromRaw(rawKey); err != nil {
 			return nil, fmt.Errorf(`failed to initialize %T from %T: %w`, k, rawKey, err)
 		}
 		return k, nil
 	case *ecdsa.PublicKey:
-		k := NewECDSAPublicKey()
+		k := newECDSAPublicKey()
 		if err := k.FromRaw(rawKey); err != nil {
 			return nil, fmt.Errorf(`failed to initialize %T from %T: %w`, k, rawKey, err)
 		}
 		return k, nil
 	case ed25519.PrivateKey:
-		k := NewOKPPrivateKey()
+		k := newOKPPrivateKey()
 		if err := k.FromRaw(rawKey); err != nil {
 			return nil, fmt.Errorf(`failed to initialize %T from %T: %w`, k, rawKey, err)
 		}
 		return k, nil
 	case ed25519.PublicKey:
-		k := NewOKPPublicKey()
+		k := newOKPPublicKey()
 		if err := k.FromRaw(rawKey); err != nil {
 			return nil, fmt.Errorf(`failed to initialize %T from %T: %w`, k, rawKey, err)
 		}
 		return k, nil
 	case x25519.PrivateKey:
-		k := NewOKPPrivateKey()
+		k := newOKPPrivateKey()
 		if err := k.FromRaw(rawKey); err != nil {
 			return nil, fmt.Errorf(`failed to initialize %T from %T: %w`, k, rawKey, err)
 		}
 		return k, nil
 	case x25519.PublicKey:
-		k := NewOKPPublicKey()
+		k := newOKPPublicKey()
 		if err := k.FromRaw(rawKey); err != nil {
 			return nil, fmt.Errorf(`failed to initialize %T from %T: %w`, k, rawKey, err)
 		}
 		return k, nil
 	case []byte:
-		k := NewSymmetricKey()
+		k := newSymmetricKey()
 		if err := k.FromRaw(rawKey); err != nil {
 			return nil, fmt.Errorf(`failed to initialize %T from %T: %w`, k, rawKey, err)
 		}
@@ -162,11 +162,12 @@ func PublicSetOf(v Set) (Set, error) {
 //
 // If `v` is a raw key, the key is first converted to a `jwk.Key`
 func PublicKeyOf(v interface{}) (Key, error) {
+	// This should catch all jwk.Key instances
 	if pk, ok := v.(PublicKeyer); ok {
 		return pk.PublicKey()
 	}
 
-	jk, err := New(v)
+	jk, err := FromRaw(v)
 	if err != nil {
 		return nil, fmt.Errorf(`failed to convert key into JWK: %w`, err)
 	}
@@ -441,7 +442,7 @@ func ParseKey(data []byte, options ...ParseOption) (Key, error) {
 		if err != nil {
 			return nil, fmt.Errorf(`failed to parse PEM encoded key: %w`, err)
 		}
-		return New(raw)
+		return FromRaw(raw)
 	}
 
 	var hint struct {
@@ -539,7 +540,7 @@ func Parse(src []byte, options ...ParseOption) (Set, error) {
 			if err != nil {
 				return nil, fmt.Errorf(`failed to parse PEM encoded key: %w`, err)
 			}
-			key, err := New(raw)
+			key, err := FromRaw(raw)
 			if err != nil {
 				return nil, fmt.Errorf(`failed to create jwk.Key from %T: %w`, raw, err)
 			}
@@ -618,19 +619,19 @@ func cloneKey(src Key) (Key, error) {
 	var dst Key
 	switch src.(type) {
 	case RSAPrivateKey:
-		dst = NewRSAPrivateKey()
+		dst = newRSAPrivateKey()
 	case RSAPublicKey:
-		dst = NewRSAPublicKey()
+		dst = newRSAPublicKey()
 	case ECDSAPrivateKey:
-		dst = NewECDSAPrivateKey()
+		dst = newECDSAPrivateKey()
 	case ECDSAPublicKey:
-		dst = NewECDSAPublicKey()
+		dst = newECDSAPublicKey()
 	case OKPPrivateKey:
-		dst = NewOKPPrivateKey()
+		dst = newOKPPrivateKey()
 	case OKPPublicKey:
-		dst = NewOKPPublicKey()
+		dst = newOKPPublicKey()
 	case SymmetricKey:
-		dst = NewSymmetricKey()
+		dst = newSymmetricKey()
 	default:
 		return nil, fmt.Errorf(`unknown key type %T`, src)
 	}
diff --git a/jwk/okp.go b/jwk/okp.go
index e6f265de..f69910f2 100644
--- a/jwk/okp.go
+++ b/jwk/okp.go
@@ -117,7 +117,7 @@ func (k *okpPrivateKey) Raw(v interface{}) error {
 func makeOKPPublicKey(v interface {
 	makePairs() []*HeaderPair
 }) (Key, error) {
-	newKey := NewOKPPublicKey()
+	newKey := newOKPPublicKey()
 
 	// Iterate and copy everything except for the bits that should not be in the public key
 	for _, pair := range v.makePairs() {
diff --git a/jwk/okp_gen.go b/jwk/okp_gen.go
index dc9d8246..b03a6f9e 100644
--- a/jwk/okp_gen.go
+++ b/jwk/okp_gen.go
@@ -47,10 +47,6 @@ type okpPublicKey struct {
 	dc                     json.DecodeCtx
 }
 
-func NewOKPPublicKey() OKPPublicKey {
-	return newOKPPublicKey()
-}
-
 func newOKPPublicKey() *okpPublicKey {
 	return &okpPublicKey{
 		mu:            &sync.RWMutex{},
@@ -584,10 +580,6 @@ type okpPrivateKey struct {
 	dc                     json.DecodeCtx
 }
 
-func NewOKPPrivateKey() OKPPrivateKey {
-	return newOKPPrivateKey()
-}
-
 func newOKPPrivateKey() *okpPrivateKey {
 	return &okpPrivateKey{
 		mu:            &sync.RWMutex{},
diff --git a/jwk/options.yaml b/jwk/options.yaml
index ae84ade5..24ad2d8b 100644
--- a/jwk/options.yaml
+++ b/jwk/options.yaml
@@ -1,5 +1,7 @@
 package_name: jwk
 output: jwk/options_gen.go
+imports:
+  - github.com/lestrrat-go/backoff/v2
 interfaces:
   - name: AutoRefreshOption
     comment: |
diff --git a/jwk/rsa.go b/jwk/rsa.go
index fc902474..46ef3896 100644
--- a/jwk/rsa.go
+++ b/jwk/rsa.go
@@ -168,7 +168,7 @@ func (k *rsaPublicKey) Raw(v interface{}) error {
 func makeRSAPublicKey(v interface {
 	makePairs() []*HeaderPair
 }) (Key, error) {
-	newKey := NewRSAPublicKey()
+	newKey := newRSAPublicKey()
 
 	// Iterate and copy everything except for the bits that should not be in the public key
 	for _, pair := range v.makePairs() {
diff --git a/jwk/rsa_gen.go b/jwk/rsa_gen.go
index a48d3496..fa4e0ed3 100644
--- a/jwk/rsa_gen.go
+++ b/jwk/rsa_gen.go
@@ -53,10 +53,6 @@ type rsaPublicKey struct {
 	dc                     json.DecodeCtx
 }
 
-func NewRSAPublicKey() RSAPublicKey {
-	return newRSAPublicKey()
-}
-
 func newRSAPublicKey() *rsaPublicKey {
 	return &rsaPublicKey{
 		mu:            &sync.RWMutex{},
@@ -595,10 +591,6 @@ type rsaPrivateKey struct {
 	dc                     json.DecodeCtx
 }
 
-func NewRSAPrivateKey() RSAPrivateKey {
-	return newRSAPrivateKey()
-}
-
 func newRSAPrivateKey() *rsaPrivateKey {
 	return &rsaPrivateKey{
 		mu:            &sync.RWMutex{},
diff --git a/jwk/symmetric.go b/jwk/symmetric.go
index 65964986..f47161e4 100644
--- a/jwk/symmetric.go
+++ b/jwk/symmetric.go
@@ -47,7 +47,7 @@ func (k *symmetricKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
 }
 
 func (k *symmetricKey) PublicKey() (Key, error) {
-	newKey := NewSymmetricKey()
+	newKey := newSymmetricKey()
 
 	for _, pair := range k.makePairs() {
 		if err := newKey.Set(pair.Key.(string), pair.Value); err != nil {
diff --git a/jwk/symmetric_gen.go b/jwk/symmetric_gen.go
index 149889f4..ecbcd5b1 100644
--- a/jwk/symmetric_gen.go
+++ b/jwk/symmetric_gen.go
@@ -43,10 +43,6 @@ type symmetricKey struct {
 	dc                     json.DecodeCtx
 }
 
-func NewSymmetricKey() SymmetricKey {
-	return newSymmetricKey()
-}
-
 func newSymmetricKey() *symmetricKey {
 	return &symmetricKey{
 		mu:            &sync.RWMutex{},


Test patch:
diff --git a/examples/jwk_example_test.go b/examples/jwk_example_test.go
index c0b5ad8f..fd7796ee 100644
--- a/examples/jwk_example_test.go
+++ b/examples/jwk_example_test.go
@@ -46,7 +46,7 @@ func ExampleJWK_Usage() {
 		_ = rawkey
 
 		// You can create jwk.Key from a raw key, too
-		fromRawKey, err := jwk.New(rawkey)
+		fromRawKey, err := jwk.FromRaw(rawkey)
 		if err != nil {
 			log.Printf("failed to acquire raw key from jwk.Key: %s", err)
 			return
@@ -58,18 +58,7 @@ func ExampleJWK_Usage() {
 			log.Printf("failed to marshal key into JSON: %s", err)
 			return
 		}
-		log.Printf("%s", jsonbuf)
 
-		// If you know the underlying Key type (RSA, EC, Symmetric), you can
-		// create an empty instance first
-		//    key := jwk.NewRSAPrivateKey()
-		// ..and then use json.Unmarshal
-		//    json.Unmarshal(key, jsonbuf)
-		//
-		// but if you don't know the type first, you have an abstract type
-		// jwk.Key, which can't be used as the first argument to json.Unmarshal
-		//
-		// In this case, use jwk.Parse()
 		fromJSONKey, err := jwk.Parse(jsonbuf)
 		if err != nil {
 			log.Printf("failed to parse json: %s", err)
@@ -88,7 +77,7 @@ func ExampleJWK_New() {
 	// []byte -> jwk.SymmetricKey
 	{
 		raw := []byte("Lorem Ipsum")
-		key, err := jwk.New(raw)
+		key, err := jwk.FromRaw(raw)
 		if err != nil {
 			fmt.Printf("failed to create symmetric key: %s\n", err)
 			return
@@ -108,7 +97,7 @@ func ExampleJWK_New() {
 			return
 		}
 
-		key, err := jwk.New(raw)
+		key, err := jwk.FromRaw(raw)
 		if err != nil {
 			fmt.Printf("failed to create RSA key: %s\n", err)
 			return
@@ -129,7 +118,7 @@ func ExampleJWK_New() {
 			return
 		}
 
-		key, err := jwk.New(raw)
+		key, err := jwk.FromRaw(raw)
 		if err != nil {
 			fmt.Printf("failed to create ECDSA key: %s\n", err)
 			return
@@ -155,7 +144,7 @@ func ExampleJWK_MarshalJSON() {
 		return
 	}
 
-	key, err := jwk.New(raw)
+	key, err := jwk.FromRaw(raw)
 	if err != nil {
 		fmt.Printf("failed to create ECDSA key: %s\n", err)
 		return
diff --git a/examples/jwk_new_example_test.go b/examples/jwk_from_raw_example_test.go
similarity index 90%
rename from examples/jwk_new_example_test.go
rename to examples/jwk_from_raw_example_test.go
index 36253568..09929665 100644
--- a/examples/jwk_new_example_test.go
+++ b/examples/jwk_from_raw_example_test.go
@@ -11,14 +11,14 @@ import (
 )
 
 func ExampleJWK_New() {
-	// First, THIS IS THE WRONG WAY TO USE jwk.New().
+	// First, THIS IS THE WRONG WAY TO USE jwk.FromRaw().
 	//
 	// Assume that the file contains a JWK in JSON format
 	//
 	//  buf, _ := os.ReadFile(file)
 	//  key, _ := json.New(buf)
 	//
-	// This is not right, because the jwk.New() function determines
+	// This is not right, because the jwk.FromRaw() function determines
 	// the type of `jwk.Key` to create based on the TYPE of the argument.
 	// In this case the type of `buf` is always []byte, and therefore
 	// it will always create a symmetric key.
@@ -34,7 +34,7 @@ func ExampleJWK_New() {
 	// []byte -> jwk.SymmetricKey
 	{
 		raw := []byte("Lorem Ipsum")
-		key, err := jwk.New(raw)
+		key, err := jwk.FromRaw(raw)
 		if err != nil {
 			fmt.Printf("failed to create symmetric key: %s\n", err)
 			return
@@ -54,7 +54,7 @@ func ExampleJWK_New() {
 			return
 		}
 
-		key, err := jwk.New(raw)
+		key, err := jwk.FromRaw(raw)
 		if err != nil {
 			fmt.Printf("failed to create symmetric key: %s\n", err)
 			return
@@ -75,7 +75,7 @@ func ExampleJWK_New() {
 			return
 		}
 
-		key, err := jwk.New(raw)
+		key, err := jwk.FromRaw(raw)
 		if err != nil {
 			fmt.Printf("failed to create symmetric key: %s\n", err)
 			return
diff --git a/examples/jwk_struct_field_example_test.go b/examples/jwk_struct_field_example_test.go
new file mode 100644
index 00000000..24c7024a
--- /dev/null
+++ b/examples/jwk_struct_field_example_test.go
@@ -0,0 +1,56 @@
+package examples_test
+
+import (
+	"encoding/json"
+	"fmt"
+	"os"
+
+	"github.com/lestrrat-go/jwx/v2/jwk"
+)
+
+type Container struct {
+	Key jwk.Key `json:"key"`
+}
+
+// This is only one way to parse a struct field whose dynamic
+// type is unknown at compile time. In this example we use
+// a proxy/wrapper to trick `Container` from attempting to
+// parse the `.Key` field, and intercept the value that
+// would have gone into the the `Container` struct into
+// `Proxy` struct's `.Key` struct field
+type Proxy struct {
+	Container
+	Key json.RawMessage `json:"key"`
+}
+
+func ExampleJWK_StructField() {
+	const src = `{
+    "key": {
+      "kty":"EC",
+      "crv":"P-256",
+      "x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
+      "y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
+      "use":"enc",
+      "kid":"1"
+	  }
+  }`
+
+	var p Proxy
+	if err := json.Unmarshal([]byte(src), &p); err != nil {
+		fmt.Printf("failed to unmarshal from JSON: %s\n", err)
+		return
+	}
+
+	// Parse the intercepted `Proxy.Key` as a `jwk.Key`
+	// and assign it to `Container.Key`
+	key, err := jwk.ParseKey(p.Key)
+	if err != nil {
+		fmt.Printf("failed to parse key: %s\n", err)
+		return
+	}
+	p.Container.Key = key
+
+	json.NewEncoder(os.Stdout).Encode(p.Container)
+	// OUTPUT:
+	// {"key":{"crv":"P-256","kid":"1","kty":"EC","use":"enc","x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4","y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"}}
+}
diff --git a/examples/jws_sign_detached_payload_example_test.go b/examples/jws_sign_detached_payload_example_test.go
index 0b6df4ed..2ebad3a1 100644
--- a/examples/jws_sign_detached_payload_example_test.go
+++ b/examples/jws_sign_detached_payload_example_test.go
@@ -11,7 +11,7 @@ import (
 func ExampleJWS_SignDetachedPayload() {
 	payload := `$.02`
 
-	key, err := jwk.New([]byte(`abracadavra`))
+	key, err := jwk.FromRaw([]byte(`abracadavra`))
 	if err != nil {
 		fmt.Printf("failed to create symmetric key: %s\n", err)
 		return
diff --git a/examples/jws_sign_example_test.go b/examples/jws_sign_example_test.go
index f2260634..8c804379 100644
--- a/examples/jws_sign_example_test.go
+++ b/examples/jws_sign_example_test.go
@@ -9,7 +9,7 @@ import (
 )
 
 func ExampleJWS_Sign() {
-	key, err := jwk.New([]byte(`abracadavra`))
+	key, err := jwk.FromRaw([]byte(`abracadavra`))
 	if err != nil {
 		fmt.Printf("failed to create key: %s\n", err)
 		return
diff --git a/examples/jws_sign_json_example_test.go b/examples/jws_sign_json_example_test.go
index 9aa9f4c5..b55c26a4 100644
--- a/examples/jws_sign_json_example_test.go
+++ b/examples/jws_sign_json_example_test.go
@@ -12,7 +12,7 @@ func ExampleJWS_SignJSON() {
 	var keys []jwk.Key
 
 	for i := 0; i < 3; i++ {
-		key, err := jwk.New([]byte(fmt.Sprintf(`abracadavra-%d`, i)))
+		key, err := jwk.FromRaw([]byte(fmt.Sprintf(`abracadavra-%d`, i)))
 		if err != nil {
 			fmt.Printf("failed to create key: %s\n", err)
 			return
diff --git a/examples/jws_verify_detached_payload_example_test.go b/examples/jws_verify_detached_payload_example_test.go
index 927fc9ec..bcd5efaa 100644
--- a/examples/jws_verify_detached_payload_example_test.go
+++ b/examples/jws_verify_detached_payload_example_test.go
@@ -12,7 +12,7 @@ func ExampleJWS_VerifyDetachedPayload() {
 	serialized := `eyJhbGciOiJIUzI1NiJ9..eOOVjre9XHILxvHaJpH-ZCb1TiiiTZLOY0Jhr7mwDns`
 	payload := `$.02`
 
-	key, err := jwk.New([]byte(`abracadavra`))
+	key, err := jwk.FromRaw([]byte(`abracadavra`))
 	if err != nil {
 		fmt.Printf("failed to create symmetric key: %s\n", err)
 		return
diff --git a/examples/jws_verify_with_key_example_test.go b/examples/jws_verify_with_key_example_test.go
index e2ea87e2..6f6ee85b 100644
--- a/examples/jws_verify_with_key_example_test.go
+++ b/examples/jws_verify_with_key_example_test.go
@@ -11,7 +11,7 @@ import (
 func ExampleJWS_VerifyWithKey() {
 	const src = `eyJhbGciOiJIUzI1NiJ9.TG9yZW0gaXBzdW0.idbECxA8ZhQbU0ddZmzdRZxQmHjwvw77lT2bwqGgNMo`
 
-	key, err := jwk.New([]byte(`abracadavra`))
+	key, err := jwk.FromRaw([]byte(`abracadavra`))
 	if err != nil {
 		fmt.Printf("failed to create key: %s\n", err)
 		return
diff --git a/examples/jws_verify_with_keyset_example_test.go b/examples/jws_verify_with_keyset_example_test.go
index 0b78cd0a..c1cdd61b 100644
--- a/examples/jws_verify_with_keyset_example_test.go
+++ b/examples/jws_verify_with_keyset_example_test.go
@@ -27,13 +27,13 @@ func ExampleJWS_VerifyWithJWKSet() {
 	// Create a JWK Set
 	set := jwk.NewSet()
 	// Add some bogus keys
-	k1, _ := jwk.New([]byte("abracadavra"))
+	k1, _ := jwk.FromRaw([]byte("abracadavra"))
 	set.Add(k1)
-	k2, _ := jwk.New([]byte("opensasame"))
+	k2, _ := jwk.FromRaw([]byte("opensasame"))
 	set.Add(k2)
 	// Add the real thing
 	pubkey, _ := jwk.PublicRawKeyOf(privkey)
-	k3, _ := jwk.New(pubkey)
+	k3, _ := jwk.FromRaw(pubkey)
 	k3.Set(jwk.AlgorithmKey, jwa.RS256)
 	set.Add(k3)
 
diff --git a/examples/jwt_example_test.go b/examples/jwt_example_test.go
index 9d7d7b51..574d54ef 100644
--- a/examples/jwt_example_test.go
+++ b/examples/jwt_example_test.go
@@ -42,7 +42,7 @@ func ExampleJWT_ParseWithJWKS() {
 		{ // Preparation:
 			// Unlike our previous example, we DO NOT want to sign the payload.
 			// Therefore we do NOT set the "kid" value
-			realKey, err := jwk.New(privKey)
+			realKey, err := jwk.FromRaw(privKey)
 			if err != nil {
 				fmt.Printf("failed to create JWK: %s\n", err)
 				return
@@ -64,7 +64,7 @@ func ExampleJWT_ParseWithJWKS() {
 
 			// Now create a key set that users will use to verity the signed payload against
 			// Normally these keys are available somewhere like https://www.googleapis.com/oauth2/v3/certs
-			pubKey, err := jwk.New(privKey.PublicKey)
+			pubKey, err := jwk.FromRaw(privKey.PublicKey)
 			if err != nil {
 				fmt.Printf("failed to create JWK: %s\n", err)
 				return
@@ -246,7 +246,7 @@ func ExampleJWT_SignToken() {
 
 	{
 		// Signing a token (using JWK)
-		jwkKey, err := jwk.New(key)
+		jwkKey, err := jwk.FromRaw(key)
 		if err != nil {
 			log.Printf("failed to create JWK key: %s", err)
 			return
@@ -355,7 +355,7 @@ func (t *TokenWithSource) MarshalJSON() ([]byte, error) {
 // verifying it, but carry the original serialized payload so that it can be
 // verified at a later point
 func ExampleJWT_Extended() {
-	key, err := jwk.New([]byte(`abracadavra`))
+	key, err := jwk.FromRaw([]byte(`abracadavra`))
 	if err != nil {
 		fmt.Printf("failed to create key: %s\n", err)
 		return
diff --git a/examples/jwt_parse_with_jku_example_test.go b/examples/jwt_parse_with_jku_example_test.go
index 907b8712..d82bc12e 100644
--- a/examples/jwt_parse_with_jku_example_test.go
+++ b/examples/jwt_parse_with_jku_example_test.go
@@ -28,7 +28,7 @@ func ExampleJWT_ParseWithJKU() {
 		}
 		// too lazy to write a proper algorithm. just assign every
 		// time, and signingKey will end up being the last key generated
-		privkey, err := jwk.New(pk)
+		privkey, err := jwk.FromRaw(pk)
 		if err != nil {
 			fmt.Printf("failed to create jwk.Key: %s\n", err)
 			return
diff --git a/examples/jwt_parse_with_keyset_example_test.go b/examples/jwt_parse_with_keyset_example_test.go
index d97c3f50..0df6dbc0 100644
--- a/examples/jwt_parse_with_keyset_example_test.go
+++ b/examples/jwt_parse_with_keyset_example_test.go
@@ -26,7 +26,7 @@ func ExampleJWT_ParseWithKeySet() {
 			return
 		}
 		// This is the key we will use to sign
-		realKey, err := jwk.New(privKey)
+		realKey, err := jwk.FromRaw(privKey)
 		if err != nil {
 			fmt.Printf("failed to create JWK: %s\n", err)
 			return
@@ -35,7 +35,11 @@ func ExampleJWT_ParseWithKeySet() {
 		realKey.Set(jwk.AlgorithmKey, jwa.RS256)
 
 		// For demonstration purposes, we also create a bogus key
-		bogusKey := jwk.NewSymmetricKey()
+		bogusKey, err := jwk.FromRaw([]byte("bogus"))
+		if err != nil {
+			fmt.Printf("failed to create bogus JWK: %s\n", err)
+			return
+		}
 		bogusKey.Set(jwk.AlgorithmKey, jwa.NoSignature)
 		bogusKey.Set(jwk.KeyIDKey, "otherkey")
 
diff --git a/examples/jwt_serialize_jwe_jws_example_test.go b/examples/jwt_serialize_jwe_jws_example_test.go
index 50bcd24e..e0a0b8b3 100644
--- a/examples/jwt_serialize_jwe_jws_example_test.go
+++ b/examples/jwt_serialize_jwe_jws_example_test.go
@@ -27,13 +27,13 @@ func ExampleJWT_SerializeJWEJWS() {
 		return
 	}
 
-	enckey, err := jwk.New(privkey.PublicKey)
+	enckey, err := jwk.FromRaw(privkey.PublicKey)
 	if err != nil {
 		fmt.Printf("failed to create symmetric key: %s\n", err)
 		return
 	}
 
-	signkey, err := jwk.New([]byte(`abracadavra`))
+	signkey, err := jwk.FromRaw([]byte(`abracadavra`))
 	if err != nil {
 		fmt.Printf("failed to create symmetric key: %s\n", err)
 		return
diff --git a/examples/jwt_serialize_jws_example_test.go b/examples/jwt_serialize_jws_example_test.go
index 5c6210e1..36d75cb6 100644
--- a/examples/jwt_serialize_jws_example_test.go
+++ b/examples/jwt_serialize_jws_example_test.go
@@ -19,7 +19,7 @@ func ExampleJWT_SerializeJWS() {
 		return
 	}
 
-	key, err := jwk.New([]byte(`abracadavra`))
+	key, err := jwk.FromRaw([]byte(`abracadavra`))
 	if err != nil {
 		fmt.Printf("failed to create symmetric key: %s\n", err)
 		return
diff --git a/internal/jwxtest/jwxtest.go b/internal/jwxtest/jwxtest.go
index 9ae99ca4..c66e75dc 100644
--- a/internal/jwxtest/jwxtest.go
+++ b/internal/jwxtest/jwxtest.go
@@ -35,7 +35,7 @@ func GenerateRsaJwk() (jwk.Key, error) {
 		return nil, fmt.Errorf(`failed to generate RSA private key: %w`, err)
 	}
 
-	k, err := jwk.New(key)
+	k, err := jwk.FromRaw(key)
 	if err != nil {
 		return nil, fmt.Errorf(`failed to generate jwk.RSAPrivateKey: %w`, err)
 	}
@@ -69,7 +69,7 @@ func GenerateEcdsaJwk() (jwk.Key, error) {
 		return nil, fmt.Errorf(`failed to generate ECDSA private key: %w`, err)
 	}
 
-	k, err := jwk.New(key)
+	k, err := jwk.FromRaw(key)
 	if err != nil {
 		return nil, fmt.Errorf(`failed to generate jwk.ECDSAPrivateKey: %w`, err)
 	}
@@ -94,7 +94,7 @@ func GenerateSymmetricKey() []byte {
 }
 
 func GenerateSymmetricJwk() (jwk.Key, error) {
-	key, err := jwk.New(GenerateSymmetricKey())
+	key, err := jwk.FromRaw(GenerateSymmetricKey())
 	if err != nil {
 		return nil, fmt.Errorf(`failed to generate jwk.SymmetricKey: %w`, err)
 	}
@@ -113,7 +113,7 @@ func GenerateEd25519Jwk() (jwk.Key, error) {
 		return nil, fmt.Errorf(`failed to generate Ed25519 private key: %w`, err)
 	}
 
-	k, err := jwk.New(key)
+	k, err := jwk.FromRaw(key)
 	if err != nil {
 		return nil, fmt.Errorf(`failed to generate jwk.OKPPrivateKey: %w`, err)
 	}
@@ -132,7 +132,7 @@ func GenerateX25519Jwk() (jwk.Key, error) {
 		return nil, fmt.Errorf(`failed to generate X25519 private key: %w`, err)
 	}
 
-	k, err := jwk.New(key)
+	k, err := jwk.FromRaw(key)
 	if err != nil {
 		return nil, fmt.Errorf(`failed to generate jwk.OKPPrivateKey: %w`, err)
 	}
diff --git a/internal/keyconv/keyconv_test.go b/internal/keyconv/keyconv_test.go
index e02b6db3..5e908ca4 100644
--- a/internal/keyconv/keyconv_test.go
+++ b/internal/keyconv/keyconv_test.go
@@ -19,7 +19,7 @@ func TestKeyconv(t *testing.T) {
 			return
 		}
 		t.Run("PrivateKey", func(t *testing.T) {
-			jwkKey, _ := jwk.New(key)
+			jwkKey, _ := jwk.FromRaw(key)
 			testcases := []struct {
 				Src   interface{}
 				Error bool
@@ -72,7 +72,7 @@ func TestKeyconv(t *testing.T) {
 		})
 		t.Run("PublicKey", func(t *testing.T) {
 			pubkey := &key.PublicKey
-			jwkKey, _ := jwk.New(pubkey)
+			jwkKey, _ := jwk.FromRaw(pubkey)
 			testcases := []struct {
 				Src   interface{}
 				Error bool
@@ -131,7 +131,7 @@ func TestKeyconv(t *testing.T) {
 		}
 
 		t.Run("PrivateKey", func(t *testing.T) {
-			jwkKey, _ := jwk.New(key)
+			jwkKey, _ := jwk.FromRaw(key)
 			testcases := []struct {
 				Src   interface{}
 				Error bool
@@ -184,7 +184,7 @@ func TestKeyconv(t *testing.T) {
 		})
 		t.Run("PublicKey", func(t *testing.T) {
 			pubkey := &key.PublicKey
-			jwkKey, _ := jwk.New(pubkey)
+			jwkKey, _ := jwk.FromRaw(pubkey)
 			testcases := []struct {
 				Src   interface{}
 				Error bool
diff --git a/jwe/headers_test.go b/jwe/headers_test.go
index 52571fbd..b9ffd1d0 100644
--- a/jwe/headers_test.go
+++ b/jwe/headers_test.go
@@ -25,13 +25,13 @@ func TestHeaders(t *testing.T) {
 	if !assert.NoError(t, err, `jwxtest.GenerateEcdsaKey should succeed`) {
 		return
 	}
-	privKey, err := jwk.New(rawKey)
-	if !assert.NoError(t, err, `jwk.New should succeed`) {
+	privKey, err := jwk.FromRaw(rawKey)
+	if !assert.NoError(t, err, `jwk.FromRaw should succeed`) {
 		return
 	}
 
-	pubKey, err := jwk.New(rawKey.PublicKey)
-	if !assert.NoError(t, err, `jwk.PublicKey should succeed`) {
+	pubKey, err := jwk.FromRaw(rawKey.PublicKey)
+	if !assert.NoError(t, err, `jwk.FromRaw should succeed`) {
 		return
 	}
 
diff --git a/jwe/jwe_test.go b/jwe/jwe_test.go
index 3d0d746b..c1724845 100644
--- a/jwe/jwe_test.go
+++ b/jwe/jwe_test.go
@@ -42,8 +42,8 @@ func init() {
       "qi":"VIMpMYbPf47dT1w_zDUXfPimsSegnMOA1zTaX7aGk_8urY6R8-ZW1FxU7AlWAyLWybqq6t16VFd7hQd0y6flUK4SlOydB61gwanOsXGOAOv82cHq0E3eL4HrtZkUuKvnPrMnsUUFlfUdybVzxyjz9JF_XyaY14ardLSjf4L_FNY"
      }`)
 
-	privkey := jwk.NewRSAPrivateKey()
-	if err := json.Unmarshal(jwkstr, privkey); err != nil {
+	privkey, err := jwk.ParseKey(jwkstr)
+	if err != nil {
 		panic(err)
 	}
 
@@ -159,8 +159,8 @@ func TestParse_RSAES_OAEP_AES_GCM(t *testing.T) {
       "qi":"VIMpMYbPf47dT1w_zDUXfPimsSegnMOA1zTaX7aGk_8urY6R8-ZW1FxU7AlWAyLWybqq6t16VFd7hQd0y6flUK4SlOydB61gwanOsXGOAOv82cHq0E3eL4HrtZkUuKvnPrMnsUUFlfUdybVzxyjz9JF_XyaY14ardLSjf4L_FNY"
      }`)
 
-	privkey := jwk.NewRSAPrivateKey()
-	if !assert.NoError(t, json.Unmarshal(jwkstr, privkey), `parsing jwk should succeed`) {
+	privkey, err := jwk.ParseKey(jwkstr)
+	if !assert.NoError(t, err, `parsing jwk should succeed`) {
 		return
 	}
 
diff --git a/jwk/headers_test.go b/jwk/headers_test.go
index be1e262b..d4bed518 100644
--- a/jwk/headers_test.go
+++ b/jwk/headers_test.go
@@ -25,7 +25,7 @@ func TestHeader(t *testing.T) {
 			"private":                     "boofoo",
 		}
 
-		h, err := jwk.New([]byte("dummy"))
+		h, err := jwk.FromRaw([]byte("dummy"))
 		if !assert.NoError(t, err, `jwk.New should succeed`) {
 			return
 		}
@@ -84,7 +84,7 @@ func TestHeader(t *testing.T) {
 			jwk.X509URLKey:                dummy,
 		}
 
-		h, err := jwk.New([]byte("dummy"))
+		h, err := jwk.FromRaw([]byte("dummy"))
 		if !assert.NoError(t, err, `jwk.New should succeed`) {
 			return
 		}
@@ -113,7 +113,7 @@ func TestHeader(t *testing.T) {
 
 	t.Run("Algorithm", func(t *testing.T) {
 		t.Parallel()
-		h, err := jwk.New([]byte("dummy"))
+		h, err := jwk.FromRaw([]byte("dummy"))
 		if !assert.NoError(t, err, `jwk.New should succeed`) {
 			return
 		}
diff --git a/jwk/iterator_test.go b/jwk/iterator_test.go
deleted file mode 100644
index 004f81e6..00000000
--- a/jwk/iterator_test.go
+++ /dev/null
@@ -1,159 +0,0 @@
-package jwk_test
-
-import (
-	"context"
-	"fmt"
-	"testing"
-
-	"github.com/lestrrat-go/jwx/v2/internal/json"
-
-	"github.com/lestrrat-go/jwx/v2/jwa"
-	"github.com/lestrrat-go/jwx/v2/jwk"
-	"github.com/stretchr/testify/assert"
-)
-
-func TestIterator(t *testing.T) {
-	commonValues := map[string]interface{}{
-		jwk.AlgorithmKey: jwa.KeyAlgorithmFrom("dummy"),
-		jwk.KeyIDKey:     "dummy-kid",
-		jwk.KeyUsageKey:  "dummy-usage",
-		jwk.KeyOpsKey:    jwk.KeyOperationList{jwk.KeyOpSign, jwk.KeyOpVerify, jwk.KeyOpEncrypt, jwk.KeyOpDecrypt, jwk.KeyOpWrapKey, jwk.KeyOpUnwrapKey, jwk.KeyOpDeriveKey, jwk.KeyOpDeriveBits},
-		"private":        "dummy-private",
-	}
-
-	verifyIterators := func(t *testing.T, v jwk.Key, expected map[string]interface{}) {
-		t.Helper()
-		t.Run("Iterate", func(t *testing.T) {
-			seen := make(map[string]interface{})
-			for iter := v.Iterate(context.TODO()); iter.Next(context.TODO()); {
-				pair := iter.Pair()
-				seen[pair.Key.(string)] = pair.Value
-
-				getV, ok := v.Get(pair.Key.(string))
-				if !assert.True(t, ok, `v.Get should succeed for key %#v`, pair.Key) {
-					return
-				}
-				if !assert.Equal(t, pair.Value, getV, `pair.Value should match value from v.Get()`) {
-					return
-				}
-			}
-			if !assert.Equal(t, expected, seen, `values should match`) {
-				return
-			}
-		})
-		t.Run("Walk", func(t *testing.T) {
-			seen := make(map[string]interface{})
-			v.Walk(context.TODO(), jwk.HeaderVisitorFunc(func(key string, value interface{}) error {
-				seen[key] = value
-				return nil
-			}))
-			if !assert.Equal(t, expected, seen, `values should match`) {
-				return
-			}
-		})
-		t.Run("AsMap", func(t *testing.T) {
-			seen, err := v.AsMap(context.TODO())
-			if !assert.NoError(t, err, `v.AsMap should succeed`) {
-				return
-			}
-			if !assert.Equal(t, expected, seen, `values should match`) {
-				return
-			}
-		})
-	}
-
-	type iterTestCase struct {
-		Extras map[string]interface{}
-		Func   func() jwk.Key
-	}
-
-	testcases := []iterTestCase{
-		{
-			Extras: map[string]interface{}{
-				jwk.RSANKey:  []byte("0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw"),
-				jwk.RSAEKey:  []byte("AQAB"),
-				jwk.RSADKey:  []byte("X4cTteJY_gn4FYPsXB8rdXix5vwsg1FLN5E3EaG6RJoVH-HLLKD9M7dx5oo7GURknchnrRweUkC7hT5fJLM0WbFAKNLWY2vv7B6NqXSzUvxT0_YSfqijwp3RTzlBaCxWp4doFk5N2o8Gy_nHNKroADIkJ46pRUohsXywbReAdYaMwFs9tv8d_cPVY3i07a3t8MN6TNwm0dSawm9v47UiCl3Sk5ZiG7xojPLu4sbg1U2jx4IBTNBznbJSzFHK66jT8bgkuqsk0GjskDJk19Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFzme1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q"),
-				jwk.RSAPKey:  []byte("83i-7IvMGXoMXCskv73TKr8637FiO7Z27zv8oj6pbWUQyLPQBQxtPVnwD20R-60eTDmD2ujnMt5PoqMrm8RfmNhVWDtjjMmCMjOpSXicFHj7XOuVIYQyqVWlWEh6dN36GVZYk93N8Bc9vY41xy8B9RzzOGVQzXvNEvn7O0nVbfs"),
-				jwk.RSAQKey:  []byte("3dfOR9cuYq-0S-mkFLzgItgMEfFzB2q3hWehMuG0oCuqnb3vobLyumqjVZQO1dIrdwgTnCdpYzBcOfW5r370AFXjiWft_NGEiovonizhKpo9VVS78TzFgxkIdrecRezsZ-1kYd_s1qDbxtkDEgfAITAG9LUnADun4vIcb6yelxk"),
-				jwk.RSADPKey: []byte("G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0"),
-				jwk.RSADQKey: []byte("s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk"),
-				jwk.RSAQIKey: []byte("GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU"),
-			},
-			Func: func() jwk.Key {
-				return jwk.NewRSAPrivateKey()
-			},
-		},
-		{
-			Extras: map[string]interface{}{
-				jwk.RSANKey: []byte("0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw"),
-				jwk.RSAEKey: []byte("AQAB"),
-			},
-			Func: func() jwk.Key {
-				return jwk.NewRSAPublicKey()
-			},
-		},
-		{
-			Extras: map[string]interface{}{
-				jwk.ECDSACrvKey: jwa.P256,
-				jwk.ECDSAXKey:   []byte("MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4"),
-				jwk.ECDSAYKey:   []byte("4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"),
-				jwk.ECDSADKey:   []byte("870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE"),
-			},
-			Func: func() jwk.Key {
-				return jwk.NewECDSAPrivateKey()
-			},
-		},
-		{
-			Extras: map[string]interface{}{
-				jwk.ECDSACrvKey: jwa.P256,
-				jwk.ECDSAXKey:   []byte("MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4"),
-				jwk.ECDSAYKey:   []byte("4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"),
-			},
-			Func: func() jwk.Key {
-				return jwk.NewECDSAPublicKey()
-			},
-		},
-		{
-			Extras: map[string]interface{}{
-				jwk.SymmetricOctetsKey: []byte("abcd"),
-			},
-			Func: func() jwk.Key {
-				return jwk.NewSymmetricKey()
-			},
-		},
-	}
-	for _, test := range testcases {
-		key := test.Func()
-		key2 := test.Func()
-		expected := make(map[string]interface{})
-		expected[jwk.KeyTypeKey] = key.KeyType()
-		for k, v := range commonValues {
-			if !assert.NoError(t, key.Set(k, v), `key.Set %#v should succeed`, k) {
-				return
-			}
-			expected[k] = v
-		}
-		for k, v := range test.Extras {
-			if !assert.NoError(t, key.Set(k, v), `key.Set %#v should succeed`, k) {
-				return
-			}
-			expected[k] = v
-		}
-
-		t.Run(fmt.Sprintf("%T", key), func(t *testing.T) {
-			verifyIterators(t, key, expected)
-		})
-		t.Run(fmt.Sprintf("%T (after json roundtripping)", key), func(t *testing.T) {
-			buf, err := json.Marshal(key)
-			if !assert.NoError(t, err, `json.Marshal should succeed`) {
-				return
-			}
-
-			if !assert.NoError(t, json.Unmarshal(buf, key2), `json.Unmarshal should succeed`) {
-				return
-			}
-
-			verifyIterators(t, key2, expected)
-		})
-	}
-}
diff --git a/jwk/jwk_test.go b/jwk/jwk_test.go
index afb1cf54..9ec12d3a 100644
--- a/jwk/jwk_test.go
+++ b/jwk/jwk_test.go
@@ -282,7 +282,7 @@ func VerifyKey(t *testing.T, def map[string]keyDef) {
 
 func TestNew(t *testing.T) {
 	t.Parallel()
-	k, err := jwk.New(nil)
+	k, err := jwk.FromRaw(nil)
 	if !assert.Nil(t, k, "key should be nil") {
 		return
 	}
@@ -878,9 +878,9 @@ func TestPublicKeyOf(t *testing.T) {
 				return
 			}
 
-			// Go through jwk.New
-			jwkKey, err := jwk.New(key.Key)
-			if !assert.NoError(t, err, `jwk.New should succeed`) {
+			// Go through jwk.FromRaw
+			jwkKey, err := jwk.FromRaw(key.Key)
+			if !assert.NoError(t, err, `jwk.FromRaw should succeed`) {
 				return
 			}
 
@@ -911,8 +911,8 @@ func TestPublicKeyOf(t *testing.T) {
 			if reflect.TypeOf(key.Key) == key.PublicKeyType {
 				continue
 			}
-			jwkKey, err := jwk.New(key.Key)
-			if !assert.NoError(t, err, `jwk.New should succeed`) {
+			jwkKey, err := jwk.FromRaw(key.Key)
+			if !assert.NoError(t, err, `jwk.FromRaw should succeed`) {
 				return
 			}
 			jwkKey.Set(jwk.KeyIDKey, fmt.Sprintf("key%d", count))
@@ -1038,8 +1038,8 @@ func TestRSA(t *testing.T) {
 			for _, raw := range []rsa.PublicKey{
 				{},
 			} {
-				_, err := jwk.New(raw)
-				if !assert.Error(t, err, `jwk.New should fail for invalid key`) {
+				_, err := jwk.FromRaw(raw)
+				if !assert.Error(t, err, `jwk.FromRaw should fail for invalid key`) {
 					return
 				}
 			}
@@ -1104,8 +1104,8 @@ func TestRSA(t *testing.T) {
 					Primes: []*big.Int{{}, {}},
 				},
 			} {
-				_, err := jwk.New(raw)
-				if !assert.Error(t, err, `jwk.New should fail for empty key`) {
+				_, err := jwk.FromRaw(raw)
+				if !assert.Error(t, err, `jwk.FromRaw should fail for empty key`) {
 					return
 				}
 			}
@@ -1159,8 +1159,8 @@ func TestECDSA(t *testing.T) {
 					},
 				},
 			} {
-				_, err := jwk.New(raw)
-				if !assert.Error(t, err, `jwk.New should fail for invalid key`) {
+				_, err := jwk.FromRaw(raw)
+				if !assert.Error(t, err, `jwk.FromRaw should fail for invalid key`) {
 					return
 				}
 			}
@@ -1199,8 +1199,8 @@ func TestECDSA(t *testing.T) {
 					X: &big.Int{},
 				},
 			} {
-				_, err := jwk.New(raw)
-				if !assert.Error(t, err, `jwk.New should fail for invalid key`) {
+				_, err := jwk.FromRaw(raw)
+				if !assert.Error(t, err, `jwk.FromRaw should fail for invalid key`) {
 					return
 				}
 			}
@@ -1233,12 +1233,12 @@ func TestECDSA(t *testing.T) {
 					return
 				}
 
-				privkey := jwk.NewECDSAPrivateKey()
-				if !assert.NoError(t, privkey.FromRaw(key), `privkey.FromRaw should succeed`) {
+				privkey, err := jwk.FromRaw(key)
+				if !assert.NoError(t, err, `jwk.FromRaw should succeed`) {
 					return
 				}
-				pubkey := jwk.NewECDSAPublicKey()
-				if !assert.NoError(t, pubkey.FromRaw(&key.PublicKey), `pubkey.FromRaw should succeed`) {
+				pubkey, err := jwk.FromRaw(key)
+				if !assert.NoError(t, err, `jwk.FromRaw should succeed`) {
 					return
 				}
 
@@ -1366,21 +1366,6 @@ func TestCustomField(t *testing.T) {
 			return
 		}
 
-		if !assert.Equal(t, expected, v, `values should match`) {
-			return
-		}
-	})
-	t.Run("json.Unmarshal", func(t *testing.T) {
-		key := jwk.NewRSAPublicKey()
-		if !assert.NoError(t, json.Unmarshal([]byte(src), key), `json.Unmarshal should succeed`) {
-			return
-		}
-
-		v, ok := key.Get(`x-birthday`)
-		if !assert.True(t, ok, `key.Get("x-birthday") should succeed`) {
-			return
-		}
-
 		if !assert.Equal(t, expected, v, `values should match`) {
 			return
 		}
@@ -1791,8 +1776,8 @@ func TestSetWithPrivateParams(t *testing.T) {
 			return
 		}
 
-		k, err := jwk.New([]byte("foobar"))
-		if !assert.NoError(t, err, `jwk.New should succeed`) {
+		k, err := jwk.FromRaw([]byte("foobar"))
+		if !assert.NoError(t, err, `jwk.FromRaw should succeed`) {
 			return
 		}
 		keys := []jwk.Key{k}
diff --git a/jwk/x5c_test.go b/jwk/x5c_test.go
index a3a20fb8..ade119ca 100644
--- a/jwk/x5c_test.go
+++ b/jwk/x5c_test.go
@@ -65,63 +65,4 @@ func Test_X5CHeader(t *testing.T) {
 			})
 		}
 	})
-
-	for _, key := range []jwk.Key{
-		jwk.NewRSAPrivateKey(),
-		jwk.NewRSAPublicKey(),
-		jwk.NewECDSAPrivateKey(),
-		jwk.NewECDSAPublicKey(),
-		jwk.NewSymmetricKey(),
-	} {
-		key := key
-		t.Run("Set X509CertChainKey", func(t *testing.T) {
-			t.Run("string", func(t *testing.T) {
-				if !assert.NoError(t, key.Set(jwk.X509CertChainKey, certs[0]), "Set for x5c should succeed") {
-					return
-				}
-
-				gotcerts, ok := key.Get(jwk.X509CertChainKey)
-				if !assert.True(t, ok, "Get for x5c should succeed") {
-					return
-				}
-
-				if !assert.Len(t, gotcerts, 1, `should have 1 cert`) {
-					return
-				}
-			})
-			t.Run("[]string", func(t *testing.T) {
-				if !assert.NoError(t, key.Set(jwk.X509CertChainKey, certs), "Set for x5c should succeed") {
-					return
-				}
-
-				gotcerts, ok := key.Get(jwk.X509CertChainKey)
-				if !assert.True(t, ok, "Get for x5c should succeed") {
-					return
-				}
-
-				if !assert.Len(t, gotcerts, 3, `should have 3 certs`) {
-					return
-				}
-			})
-			t.Run("[]interface{} with string elements", func(t *testing.T) {
-				tmp := make([]interface{}, len(certs))
-				for i := 0; i < len(certs); i++ {
-					tmp[i] = certs[i]
-				}
-
-				if !assert.NoError(t, key.Set(jwk.X509CertChainKey, tmp), "Set for x5c should succeed") {
-					return
-				}
-
-				gotcerts, ok := key.Get(jwk.X509CertChainKey)
-				if !assert.True(t, ok, "Get for x5c should succeed") {
-					return
-				}
-
-				if !assert.Len(t, gotcerts, 3, `should have 3 certs`) {
-					return
-				}
-			})
-		})
-	}
 }
diff --git a/jws/es256k_test.go b/jws/es256k_test.go
index a103fd0a..d5cb3b5d 100644
--- a/jws/es256k_test.go
+++ b/jws/es256k_test.go
@@ -23,7 +23,7 @@ func TestES256K(t *testing.T) {
 	if !assert.NoError(t, err, "ECDSA key generated") {
 		return
 	}
-	jwkKey, _ := jwk.New(key.PublicKey)
+	jwkKey, _ := jwk.FromRaw(key.PublicKey)
 	keys := map[string]interface{}{
 		"Verify(ecdsa.PublicKey)":  key.PublicKey,
 		"Verify(*ecdsa.PublicKey)": &key.PublicKey,
diff --git a/jws/jws_test.go b/jws/jws_test.go
index ff8e2b30..a81ac956 100644
--- a/jws/jws_test.go
+++ b/jws/jws_test.go
@@ -45,7 +45,7 @@ func TestSanity(t *testing.T) {
     "kty": "oct",
     "k": "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow"
   }`))
-		if !assert.NoError(t, err, `jwk.New should succeed`) {
+		if !assert.NoError(t, err, `jwk.ParseKey should succeed`) {
 			return
 		}
 
@@ -203,7 +203,7 @@ func (es *dummyECDSACryptoSigner) Sign(rand io.Reader, digest []byte, opts crypt
 var _ crypto.Signer = &dummyECDSACryptoSigner{}
 
 func testRoundtrip(t *testing.T, payload []byte, alg jwa.SignatureAlgorithm, signKey interface{}, keys map[string]interface{}) {
-	jwkKey, err := jwk.New(signKey)
+	jwkKey, err := jwk.FromRaw(signKey)
 	if !assert.NoError(t, err, `jwk.New should succeed`) {
 		return
 	}
@@ -294,7 +294,7 @@ func TestRoundtrip(t *testing.T) {
 	t.Run("HMAC", func(t *testing.T) {
 		t.Parallel()
 		sharedkey := []byte("Avracadabra")
-		jwkKey, _ := jwk.New(sharedkey)
+		jwkKey, _ := jwk.FromRaw(sharedkey)
 		keys := map[string]interface{}{
 			"[]byte":  sharedkey,
 			"jwk.Key": jwkKey,
@@ -314,7 +314,7 @@ func TestRoundtrip(t *testing.T) {
 		if !assert.NoError(t, err, "ECDSA key generated") {
 			return
 		}
-		jwkKey, _ := jwk.New(key.PublicKey)
+		jwkKey, _ := jwk.FromRaw(key.PublicKey)
 		keys := map[string]interface{}{
 			"Verify(ecdsa.PublicKey)":  key.PublicKey,
 			"Verify(*ecdsa.PublicKey)": &key.PublicKey,
@@ -334,7 +334,7 @@ func TestRoundtrip(t *testing.T) {
 		if !assert.NoError(t, err, "RSA key generated") {
 			return
 		}
-		jwkKey, _ := jwk.New(key.PublicKey)
+		jwkKey, _ := jwk.FromRaw(key.PublicKey)
 		keys := map[string]interface{}{
 			"Verify(rsa.PublicKey)":  key.PublicKey,
 			"Verify(*rsa.PublicKey)": &key.PublicKey,
@@ -355,7 +355,7 @@ func TestRoundtrip(t *testing.T) {
 			return
 		}
 		pubkey := key.Public()
-		jwkKey, _ := jwk.New(pubkey)
+		jwkKey, _ := jwk.FromRaw(pubkey)
 		keys := map[string]interface{}{
 			"Verify(ed25519.Public())": pubkey,
 			// Meh, this doesn't work
@@ -575,8 +575,8 @@ func TestEncode(t *testing.T) {
     "qi":"IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2oy26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLUW0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U"
   }`
 
-		privkey := jwk.NewRSAPrivateKey()
-		if !assert.NoError(t, json.Unmarshal([]byte(jwksrc), privkey), `parsing jwk should be successful`) {
+		privkey, err := jwk.ParseKey([]byte(jwksrc))
+		if !assert.NoError(t, err, `parsing jwk should be successful`) {
 			return
 		}
 
@@ -652,8 +652,8 @@ func TestEncode(t *testing.T) {
     "y":"x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0",
     "d":"jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI"
   }`
-		privkey := jwk.NewECDSAPrivateKey()
-		if !assert.NoError(t, json.Unmarshal([]byte(jwksrc), privkey), `parsing jwk should succeed`) {
+		privkey, err := jwk.ParseKey([]byte(jwksrc))
+		if !assert.NoError(t, err, `parsing jwk should succeed`) {
 			return
 		}
 
@@ -737,8 +737,8 @@ func TestEncode(t *testing.T) {
 			return
 		}
 
-		privkey := jwk.NewOKPPrivateKey()
-		if !assert.NoError(t, json.Unmarshal([]byte(jwksrc), privkey), `parsing jwk should succeed`) {
+		privkey, err := jwk.ParseKey([]byte(jwksrc))
+		if !assert.NoError(t, err, `parsing jwk should succeed`) {
 			return
 		}
 
@@ -999,7 +999,7 @@ func TestPublicHeaders(t *testing.T) {
 	_ = signer // TODO
 
 	pubkey := key.PublicKey
-	pubjwk, err := jwk.New(&pubkey)
+	pubjwk, err := jwk.FromRaw(&pubkey)
 	if !assert.NoError(t, err, "NewRsaPublicKey should succeed") {
 		return
 	}
@@ -1016,8 +1016,8 @@ func TestDecode_ES384Compact_NoSigTrim(t *testing.T) {
     "y":"CRKSqP-aYTIsqJfg_wZEEYUayUR5JhZaS2m4NLk2t1DfXZgfApAJ2lBO0vWKnUMp"
   }`
 
-	pubkey := jwk.NewECDSAPublicKey()
-	if !assert.NoError(t, json.Unmarshal([]byte(jwksrc), pubkey), `parsing jwk should be successful`) {
+	pubkey, err := jwk.ParseKey([]byte(jwksrc))
+	if !assert.NoError(t, err, `parsing jwk should be successful`) {
 		return
 	}
 
@@ -1073,9 +1073,9 @@ func TestVerifySet(t *testing.T) {
 
 	makeSet := func(privkey jwk.Key) jwk.Set {
 		set := jwk.NewSet()
-		k1, _ := jwk.New([]byte("abracadavra"))
+		k1, _ := jwk.FromRaw([]byte("abracadavra"))
 		set.Add(k1)
-		k2, _ := jwk.New([]byte("opensasame"))
+		k2, _ := jwk.FromRaw([]byte("opensasame"))
 		set.Add(k2)
 		pubkey, _ := jwk.PublicKeyOf(privkey)
 		pubkey.Set(jwk.AlgorithmKey, jwa.RS256)
@@ -1421,117 +1421,6 @@ func TestRFC7797(t *testing.T) {
 	})
 }
 
-func TestAlgorithmsForKey(t *testing.T) {
-	testcases := []struct {
-		Name     string
-		Key      interface{}
-		Expected []jwa.SignatureAlgorithm
-	}{
-		{
-			Name:     "Octet sequence",
-			Key:      []byte("hello"),
-			Expected: []jwa.SignatureAlgorithm{jwa.HS256, jwa.HS384, jwa.HS512},
-		},
-		{
-			Name:     "rsa.PublicKey",
-			Key:      rsa.PublicKey{},
-			Expected: []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512},
-		},
-		{
-			Name:     "*rsa.PublicKey",
-			Key:      &rsa.PublicKey{},
-			Expected: []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512},
-		},
-		{
-			Name:     "jwk.RSAPublicKey",
-			Key:      jwk.NewRSAPublicKey(),
-			Expected: []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512},
-		},
-		{
-			Name:     "ecdsa.PublicKey",
-			Key:      ecdsa.PublicKey{},
-			Expected: []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512},
-		},
-		{
-			Name:     "*ecdsa.PublicKey",
-			Key:      &ecdsa.PublicKey{},
-			Expected: []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512},
-		},
-		{
-			Name:     "jwk.ECDSAPublicKey",
-			Key:      jwk.NewECDSAPublicKey(),
-			Expected: []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512},
-		},
-		{
-			Name:     "rsa.PrivateKey",
-			Key:      rsa.PrivateKey{},
-			Expected: []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512},
-		},
-		{
-			Name:     "*rsa.PrivateKey",
-			Key:      &rsa.PrivateKey{},
-			Expected: []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512},
-		},
-		{
-			Name:     "jwk.RSAPrivateKey",
-			Key:      jwk.NewRSAPrivateKey(),
-			Expected: []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512},
-		},
-		{
-			Name:     "ecdsa.PrivateKey",
-			Key:      ecdsa.PrivateKey{},
-			Expected: []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512},
-		},
-		{
-			Name:     "*ecdsa.PrivateKey",
-			Key:      &ecdsa.PrivateKey{},
-			Expected: []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512},
-		},
-		{
-			Name:     "jwk.ECDSAPrivateKey",
-			Key:      jwk.NewECDSAPrivateKey(),
-			Expected: []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512},
-		},
-		{
-			Name:     "ed25519.PublicKey",
-			Key:      ed25519.PublicKey(nil),
-			Expected: []jwa.SignatureAlgorithm{jwa.EdDSA},
-		},
-		{
-			Name:     "x25519.PublicKey",
-			Key:      x25519.PublicKey(nil),
-			Expected: []jwa.SignatureAlgorithm{jwa.EdDSA},
-		},
-	}
-
-	for _, tc := range testcases {
-		tc := tc
-
-		if hasES256K {
-			if strings.Contains(strings.ToLower(tc.Name), `ecdsa`) {
-				tc.Expected = append(tc.Expected, jwa.ES256K)
-			}
-		}
-
-		sort.Slice(tc.Expected, func(i, j int) bool {
-			return tc.Expected[i].String() < tc.Expected[j].String()
-		})
-		t.Run(tc.Name, func(t *testing.T) {
-			algs, err := jws.AlgorithmsForKey(tc.Key)
-			if !assert.NoError(t, err, `jws.AlgorithmsForKey should succeed`) {
-				return
-			}
-
-			sort.Slice(algs, func(i, j int) bool {
-				return algs[i].String() < algs[j].String()
-			})
-			if !assert.Equal(t, tc.Expected, algs, `results should match`) {
-				return
-			}
-		})
-	}
-}
-
 func TestGH485(t *testing.T) {
 	const payload = `eyJhIjoiYiJ9`
 	const protected = `eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImNyaXQiOlsiZXhwIl0sImV4cCI6MCwiaXNzIjoiZm9vIiwibmJmIjowLCJpYXQiOjB9`
@@ -1824,3 +1713,132 @@ func TestJKU(t *testing.T) {
 		}
 	})
 }
+
+func TestAlgorithmsForKey(t *testing.T) {
+	rsaprivkey, err := jwxtest.GenerateRsaJwk()
+	if !assert.NoError(t, err, `jwxtest.GenerateRsaPrivateKey should succeed`) {
+		return
+	}
+	rsapubkey, err := rsaprivkey.PublicKey()
+	if !assert.NoError(t, err, `jwk (RSA) PublicKey() should succeed`) {
+		return
+	}
+
+	ecdsaprivkey, err := jwxtest.GenerateEcdsaJwk()
+	if !assert.NoError(t, err, `jwxtest.GenerateEcdsaPrivateKey should succeed`) {
+		return
+	}
+	ecdsapubkey, err := ecdsaprivkey.PublicKey()
+	if !assert.NoError(t, err, `jwk (ECDSA) PublicKey() should succeed`) {
+		return
+	}
+
+	testcases := []struct {
+		Name     string
+		Key      interface{}
+		Expected []jwa.SignatureAlgorithm
+	}{
+		{
+			Name:     "Octet sequence",
+			Key:      []byte("hello"),
+			Expected: []jwa.SignatureAlgorithm{jwa.HS256, jwa.HS384, jwa.HS512},
+		},
+		{
+			Name:     "rsa.PublicKey",
+			Key:      rsa.PublicKey{},
+			Expected: []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512},
+		},
+		{
+			Name:     "*rsa.PublicKey",
+			Key:      &rsa.PublicKey{},
+			Expected: []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512},
+		},
+		{
+			Name:     "jwk.RSAPublicKey",
+			Key:      rsapubkey,
+			Expected: []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512},
+		},
+		{
+			Name:     "ecdsa.PublicKey",
+			Key:      ecdsa.PublicKey{},
+			Expected: []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512},
+		},
+		{
+			Name:     "*ecdsa.PublicKey",
+			Key:      &ecdsa.PublicKey{},
+			Expected: []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512},
+		},
+		{
+			Name:     "jwk.ECDSAPublicKey",
+			Key:      ecdsapubkey,
+			Expected: []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512},
+		},
+		{
+			Name:     "rsa.PrivateKey",
+			Key:      rsa.PrivateKey{},
+			Expected: []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512},
+		},
+		{
+			Name:     "*rsa.PrivateKey",
+			Key:      &rsa.PrivateKey{},
+			Expected: []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512},
+		},
+		{
+			Name:     "jwk.RSAPrivateKey",
+			Key:      rsapubkey,
+			Expected: []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512},
+		},
+		{
+			Name:     "ecdsa.PrivateKey",
+			Key:      ecdsa.PrivateKey{},
+			Expected: []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512},
+		},
+		{
+			Name:     "*ecdsa.PrivateKey",
+			Key:      &ecdsa.PrivateKey{},
+			Expected: []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512},
+		},
+		{
+			Name:     "jwk.ECDSAPrivateKey",
+			Key:      ecdsaprivkey,
+			Expected: []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512},
+		},
+		{
+			Name:     "ed25519.PublicKey",
+			Key:      ed25519.PublicKey(nil),
+			Expected: []jwa.SignatureAlgorithm{jwa.EdDSA},
+		},
+		{
+			Name:     "x25519.PublicKey",
+			Key:      x25519.PublicKey(nil),
+			Expected: []jwa.SignatureAlgorithm{jwa.EdDSA},
+		},
+	}
+
+	for _, tc := range testcases {
+		tc := tc
+
+		if hasES256K {
+			if strings.Contains(strings.ToLower(tc.Name), `ecdsa`) {
+				tc.Expected = append(tc.Expected, jwa.ES256K)
+			}
+		}
+
+		sort.Slice(tc.Expected, func(i, j int) bool {
+			return tc.Expected[i].String() < tc.Expected[j].String()
+		})
+		t.Run(tc.Name, func(t *testing.T) {
+			algs, err := jws.AlgorithmsForKey(tc.Key)
+			if !assert.NoError(t, err, `jws.AlgorithmsForKey should succeed`) {
+				return
+			}
+
+			sort.Slice(algs, func(i, j int) bool {
+				return algs[i].String() < algs[j].String()
+			})
+			if !assert.Equal(t, tc.Expected, algs, `results should match`) {
+				return
+			}
+		})
+	}
+}
diff --git a/jwt/jwt_test.go b/jwt/jwt_test.go
index b944d7e7..9f831562 100644
--- a/jwt/jwt_test.go
+++ b/jwt/jwt_test.go
@@ -389,12 +389,12 @@ func TestJWTParseVerify(t *testing.T) {
 		})
 		t.Run("UseDefault with multiple keys should fail", func(t *testing.T) {
 			t.Parallel()
-			pubkey1 := jwk.NewRSAPublicKey()
-			if !assert.NoError(t, pubkey1.FromRaw(&key.PublicKey)) {
+			pubkey1, err := jwk.FromRaw(&key.PublicKey)
+			if !assert.NoError(t, err) {
 				return
 			}
-			pubkey2 := jwk.NewRSAPublicKey()
-			if !assert.NoError(t, pubkey2.FromRaw(&key.PublicKey)) {
+			pubkey2, err := jwk.FromRaw(&key.PublicKey)
+			if !assert.NoError(t, err) {
 				return
 			}
 
@@ -442,8 +442,8 @@ func TestJWTParseVerify(t *testing.T) {
 
 			signedButNot := bytes.Join([][]byte{dummyEncoded, payload, signature}, []byte{'.'})
 
-			pubkey := jwk.NewRSAPublicKey()
-			if !assert.NoError(t, pubkey.FromRaw(&key.PublicKey)) {
+			pubkey, err := jwk.FromRaw(&key.PublicKey)
+			if !assert.NoError(t, err) {
 				return
 			}
 
@@ -653,8 +653,7 @@ func TestSignJWK(t *testing.T) {
 	priv, err := jwxtest.GenerateRsaKey()
 	assert.Nil(t, err)
 
-	key := jwk.NewRSAPrivateKey()
-	err = key.FromRaw(priv)
+	key, err := jwk.FromRaw(priv)
 	assert.Nil(t, err)
 
 	key.Set(jwk.KeyIDKey, "test")