...

Text file src/github.com/lestrrat-go/jwx/jwk/README.md

Documentation: github.com/lestrrat-go/jwx/jwk

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

View as plain text