jwk

package
v1.0.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 11, 2021 License: MIT Imports: 35 Imported by: 602

Documentation

Overview

Package jwk implements JWK as described in https://tools.ietf.org/html/rfc7517

Example
package main

import (
	"log"

	"github.com/lestrrat-go/jwx/jwk"
)

func main() {
	set, err := jwk.Fetch("https://foobar.domain/json")
	if err != nil {
		log.Printf("failed to parse JWK: %s", err)
		return
	}

	// If you KNOW you have exactly one key, you can just
	// use set.Keys[0]
	keys := set.LookupKeyID("mykey")
	if len(keys) == 0 {
		log.Printf("failed to lookup key: %s", err)
		return
	}

	var key interface{}
	if err := keys[0].Raw(&key); err != nil {
		log.Printf("failed to generate public key: %s", err)
		return
	}
	// Use key for jws.Verify() or whatever
	_ = key
}
Output:

Index

Examples

Constants

View Source
const (
	ECDSACrvKey = "crv"
	ECDSADKey   = "d"
	ECDSAXKey   = "x"
	ECDSAYKey   = "y"
)
View Source
const (
	KeyTypeKey                = "kty"
	KeyUsageKey               = "use"
	KeyOpsKey                 = "key_ops"
	AlgorithmKey              = "alg"
	KeyIDKey                  = "kid"
	X509URLKey                = "x5u"
	X509CertChainKey          = "x5c"
	X509CertThumbprintKey     = "x5t"
	X509CertThumbprintS256Key = "x5t#S256"
)
View Source
const (
	OKPCrvKey = "crv"
	OKPDKey   = "d"
	OKPXKey   = "x"
)
View Source
const (
	RSADKey  = "d"
	RSADPKey = "dp"
	RSADQKey = "dq"
	RSAEKey  = "e"
	RSANKey  = "n"
	RSAPKey  = "p"
	RSAQKey  = "q"
	RSAQIKey = "qi"
)
View Source
const (
	SymmetricOctetsKey = "k"
)

Variables

This section is empty.

Functions

func AssignKeyID added in v1.0.2

func AssignKeyID(key Key, options ...Option) error

AssignKeyID is a convenience function to automatically assign the "kid" section of the key, if it already doesn't have one. It uses Key.Thumbprint method with crypto.SHA256 as the default hashing algorithm

func ParseRawKey added in v1.0.6

func ParseRawKey(data []byte, rawkey interface{}) error

ParseRawKey is a combination of ParseKey and Raw. It parses a single JWK key, and assigns the "raw" key to the given parameter. The key must either be a pointer to an empty interface, or a pointer to the actual raw key type such as *rsa.PrivateKey, *ecdsa.PublicKey, *[]byte, etc.

func PublicKeyOf added in v1.0.0

func PublicKeyOf(v interface{}) (interface{}, error)

PublicKeyOf returns the corresponding public key of the given value `v`. For example, if v is a `*rsa.PrivateKey`, then `*rsa.PublicKey` is returned.

Not to be confused with jwk.Key.PubliKey(). In hindsight, this should have been named PublicRawKeyOf() or some such.

If given a public key, then the same public key will be returned. For example, if v is a `*rsa.PublicKey`, then the same value is returned.

If v is of a type that we don't support, an error is returned.

This is useful when you are dealing with the jwk.Key interface alone and you don't know before hand what the underlying key type is, but you still want to obtain the corresponding public key

Types

type AutoRefresh added in v1.0.7

type AutoRefresh struct {
	// contains filtered or unexported fields
}

AutoRefresh is a container that keeps track of *jwk.Set object by their source URLs. The *jwk.Set objects are refreshed automatically behind the scenes.

Before retrieving the *jwk.Set objects, the user must pre-register the URLs they intend to use by calling `Configure()`

ar := jwk.NewAutoRefresh(ctx)
ar.Configure(url, options...)

Once registered, you can call `Fetch()` to retrieve the *jwk.Set object.

All JWKS objects that are retrieved via the auto-fetch mechanism should be treated read-only, as they are shared among the consumers and this object.

func NewAutoRefresh added in v1.0.7

func NewAutoRefresh(ctx context.Context) *AutoRefresh

NewAutoRefresh creates a container that keeps track of JWKS objects which are automatically refreshed.

The context object in the argument controls the life-span of the auto-refresh worker. If you are using this in a long running process, this should mostly be set to a context that ends when the main loop/part of your program exits:

func MainLoop() {
  ctx, cancel := context.WithCancel(context.Background())
  defer cancel()
  ar := jwk.AutoRefresh(ctx)
  for ... {
    ...
  }
}

func (*AutoRefresh) Configure added in v1.0.7

func (af *AutoRefresh) Configure(url string, options ...AutoRefreshOption)

Configure registers the url to be controlled by AutoRefresh, and also sets any options associated to it.

Note that options are treated as a whole -- you can't just update one value. For example, if you did:

ar.Configure(url, jwk.WithHTTPClient(...))
ar.Configure(url, jwk.WithRefreshInterval(...))

The the end result is that `url` is ONLY associated with the options given in the second call to `Configure()`, i.e. `jwk.WithRefreshInterval`. The other unspecified options, including the HTTP client, is set to their default values.

Configuration must propagate between goroutines, and therefore are not atomic (But changes should be felt "soon enough" for practical purposes)

func (*AutoRefresh) Fetch added in v1.0.7

func (af *AutoRefresh) Fetch(ctx context.Context, url string) (*Set, error)

If it has previously been fetched, then a cached value is returned. If this the first time `url` was requested, an HTTP request will be sent, synchronously. Also, when accessed via multiple goroutines concurrently, the first time around only one goroutine will be allowed to perform the initialization (HTTP fetch and cache population). All other goroutines will be blocked until the operation is completed.

DO NOT modify the *jwk.Set object returned by this method, as the objects are shared among all consumers and the backend goroutine

func (*AutoRefresh) Refresh added in v1.0.7

func (af *AutoRefresh) Refresh(ctx context.Context, url string) (*Set, error)

Refresh is the same as Fetch(), except that HTTP fetching is done synchronously. This is useful for when you want to force an HTTP fetch instead of waiting for the background goroutine to do it, for example when you want to make sure the AutoRefresh cache is warmed up before starting your main loop

func (*AutoRefresh) Snapshot added in v1.0.7

func (af *AutoRefresh) Snapshot() <-chan TargetSnapshot

type AutoRefreshOption added in v1.0.7

type AutoRefreshOption interface {
	Option
	// contains filtered or unexported methods
}

func WithHTTPClient

func WithHTTPClient(cl *http.Client) AutoRefreshOption

WithHTTPClient allows users to specify the "net/http".Client object that is used when fetching *jwk.Set objects.

For historical reasons this method is also used in `jwk.Fetch*` functions, eventhough the return value is marked as an `AutoRefreshOption`

func WithMinRefreshInterval added in v1.0.7

func WithMinRefreshInterval(d time.Duration) AutoRefreshOption

WithMinRefreshInterval specifies the minimum refresh interval to be used when using AutoRefresh. This value is ONLY used if you did not specify a user-supplied static refresh interval via `WithRefreshInterval`.

This value is used as a fallback value when tokens are refreshed.

When we fetch the key from a remote URL, we first look at the max-age directive from Cache-Control response header. If this value is present, we compare the max-age value and the value specified by this option and take the larger one.

Next we check for the Expires header, and similarly if the header is present, we compare it against the value specified by this option, and take the larger one.

Finally, if neither of the above headers are present, we use the value specified by this option as the next refresh timing

If unspecified, the minimum refresh interval is 1 hour

func WithRefreshBackoff added in v1.0.7

func WithRefreshBackoff(v backoff.Policy) AutoRefreshOption

WithRefreshRetryBackoff specifies the backoff policy to use when refreshing a JWKS from a remote server fails. This does not have any effect on initial `Fetch()`, or any of the `Refresh()` calls -- the backoff is applied ONLY on the background refreshing goroutine.

func WithRefreshInterval added in v1.0.7

func WithRefreshInterval(d time.Duration) AutoRefreshOption

WithRefreshInterval specifies the static interval between refreshes of *jwk.Set objects controlled by jwk.AutoRefresh.

Providing this option overrides the adaptive token refreshing based on Cache-Control/Expires header (and jwk.WithMinRefreshInterval), and refreshes will *always* happen in this interval.

type CertificateChain

type CertificateChain struct {
	// contains filtered or unexported fields
}

func (*CertificateChain) Accept

func (c *CertificateChain) Accept(v interface{}) error

func (CertificateChain) Get

func (c CertificateChain) Get() []*x509.Certificate

func (CertificateChain) MarshalJSON added in v1.0.0

func (c CertificateChain) MarshalJSON() ([]byte, error)

func (*CertificateChain) UnmarshalJSON added in v1.0.0

func (c *CertificateChain) UnmarshalJSON(buf []byte) error

type ECDSAPrivateKey

type ECDSAPrivateKey interface {
	Key
	FromRaw(*ecdsa.PrivateKey) error
	Crv() jwa.EllipticCurveAlgorithm
	D() []byte
	X() []byte
	Y() []byte
	PublicKey() (ECDSAPublicKey, error)
}

func NewECDSAPrivateKey added in v1.0.0

func NewECDSAPrivateKey() ECDSAPrivateKey

type ECDSAPublicKey

type ECDSAPublicKey interface {
	Key
	FromRaw(*ecdsa.PublicKey) error
	Crv() jwa.EllipticCurveAlgorithm
	X() []byte
	Y() []byte
}

func NewECDSAPublicKey added in v1.0.0

func NewECDSAPublicKey() ECDSAPublicKey

type HeaderIterator added in v1.0.0

type HeaderIterator = mapiter.Iterator

type HeaderPair added in v1.0.0

type HeaderPair = mapiter.Pair

type HeaderVisitor added in v1.0.0

type HeaderVisitor = iter.MapVisitor

type HeaderVisitorFunc added in v1.0.0

type HeaderVisitorFunc = iter.MapVisitorFunc

type Key

type Key interface {
	// Get returns the value of a single field. The second boolean return value
	// will be false if the field is not stored in the source
	//
	// This method, which returns an `interface{}`, exists because
	// these objects can contain extra _arbitrary_ fields that users can
	// specify, and there is no way of knowing what type they could be
	Get(string) (interface{}, bool)

	// Set sets the value of a single field. Note that certain fields,
	// notably "kty" cannot be altered, but will not return an error
	//
	// This method, which takes an `interface{}`, exists because
	// these objects can contain extra _arbitrary_ fields that users can
	// specify, and there is no way of knowing what type they could be
	Set(string, interface{}) error

	// Raw creates the corresponding raw key. For example,
	// EC types would create *ecdsa.PublicKey or *ecdsa.PrivateKey,
	// and OctetSeq types create a []byte key.
	//
	// If you do not know the exact type of a jwk.Key before attempting
	// to obtain the raw key, you can simply pass a pointer to an
	// empty interface as the first argument.
	//
	// If you already know the exact type, it is recommended that you
	// pass a pointer to the actual key type (e.g. *rsa.PrivateKey, *ecdsa.PublicKey
	// for efficiency
	Raw(interface{}) error

	// Thumbprint returns the JWK thumbprint using the indicated
	// hashing algorithm, according to RFC 7638
	Thumbprint(crypto.Hash) ([]byte, error)

	// Iterate returns an iterator that returns all keys and values
	Iterate(ctx context.Context) HeaderIterator

	// Walk is a utility tool that allows a visitor to iterate all keys and values
	Walk(context.Context, HeaderVisitor) error

	// AsMap is a utility tool returns a map that contains the same fields as the source
	AsMap(context.Context) (map[string]interface{}, error)

	// PrivateParams returns the non-standard elements in the source structure
	// WARNING: DO NOT USE PrivateParams() IF YOU HAVE CONCURRENT CODE ACCESSING THEM.
	// Use AsMap() to get a copy of the entire header instead
	PrivateParams() map[string]interface{}

	KeyType() jwa.KeyType
	KeyUsage() string
	KeyOps() KeyOperationList
	Algorithm() string
	KeyID() string
	X509URL() string
	X509CertChain() []*x509.Certificate
	X509CertThumbprint() string
	X509CertThumbprintS256() string
}

Key defines the minimal interface for each of the key types. Their use and implementation differ significantly between each key types, so you should use type assertions to perform more specific tasks with each key

func New

func New(key interface{}) (Key, error)

New 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:

* "crypto/rsa".PrivateKey and "crypto/rsa".PublicKey creates an RSA based key * "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 ParseKey added in v1.0.0

func ParseKey(data []byte) (Key, error)

ParseKey parses a single key JWK. This method will report failure for JWK with multiple keys, even if the JWK is valid: You must specify a single key only.

type KeyIterator added in v1.0.0

type KeyIterator = arrayiter.Iterator

type KeyOperation

type KeyOperation string
const (
	KeyOpSign       KeyOperation = "sign"       // (compute digital signature or MAC)
	KeyOpVerify     KeyOperation = "verify"     // (verify digital signature or MAC)
	KeyOpEncrypt    KeyOperation = "encrypt"    // (encrypt content)
	KeyOpDecrypt    KeyOperation = "decrypt"    // (decrypt content and validate decryption, if applicable)
	KeyOpWrapKey    KeyOperation = "wrapKey"    // (encrypt key)
	KeyOpUnwrapKey  KeyOperation = "unwrapKey"  // (decrypt key and validate decryption, if applicable)
	KeyOpDeriveKey  KeyOperation = "deriveKey"  // (derive key)
	KeyOpDeriveBits KeyOperation = "deriveBits" // (derive bits not to be used as a key)
)

type KeyOperationList

type KeyOperationList []KeyOperation

func (*KeyOperationList) Accept

func (ops *KeyOperationList) Accept(v interface{}) error

func (*KeyOperationList) Get

type KeyPair added in v1.0.0

type KeyPair = arrayiter.Pair

type KeyUsageType

type KeyUsageType string

KeyUsageType is used to denote what this key should be used for

const (
	// ForSignature is the value used in the headers to indicate that
	// this key should be used for signatures
	ForSignature KeyUsageType = "sig"
	// ForEncryption is the value used in the headers to indicate that
	// this key should be used for encrypting
	ForEncryption KeyUsageType = "enc"
)

func (*KeyUsageType) Accept added in v1.0.7

func (k *KeyUsageType) Accept(v interface{}) error

func (KeyUsageType) String added in v1.0.7

func (k KeyUsageType) String() string

type OKPPrivateKey added in v1.0.7

type OKPPrivateKey interface {
	Key
	FromRaw(interface{}) error
	Crv() jwa.EllipticCurveAlgorithm
	D() []byte
	X() []byte
	PublicKey() (OKPPublicKey, error)
}

func NewOKPPrivateKey added in v1.0.7

func NewOKPPrivateKey() OKPPrivateKey

type OKPPublicKey added in v1.0.7

type OKPPublicKey interface {
	Key
	FromRaw(interface{}) error
	Crv() jwa.EllipticCurveAlgorithm
	X() []byte
}

func NewOKPPublicKey added in v1.0.7

func NewOKPPublicKey() OKPPublicKey

type Option

type Option = option.Interface

func WithThumbprintHash added in v1.0.2

func WithThumbprintHash(h crypto.Hash) Option

type RSAPrivateKey

type RSAPrivateKey interface {
	Key
	FromRaw(*rsa.PrivateKey) error
	D() []byte
	DP() []byte
	DQ() []byte
	E() []byte
	N() []byte
	P() []byte
	Q() []byte
	QI() []byte
	PublicKey() (RSAPublicKey, error)
}

func NewRSAPrivateKey added in v1.0.0

func NewRSAPrivateKey() RSAPrivateKey

type RSAPublicKey

type RSAPublicKey interface {
	Key
	FromRaw(*rsa.PublicKey) error
	E() []byte
	N() []byte
}

func NewRSAPublicKey added in v1.0.0

func NewRSAPublicKey() RSAPublicKey

type Set

type Set struct {
	Keys []Key
}

Set is a convenience struct to allow generating and parsing JWK sets as opposed to single JWKs

func Fetch

func Fetch(urlstring string, options ...Option) (*Set, error)

Fetch fetches a JWK resource specified by a URL

func FetchHTTP

func FetchHTTP(jwkurl string, options ...Option) (*Set, error)

FetchHTTP wraps FetchHTTPWithContext using the background context.

func FetchHTTPWithContext added in v0.9.2

func FetchHTTPWithContext(ctx context.Context, jwkurl string, options ...Option) (*Set, error)

FetchHTTPWithContext fetches the remote JWK and parses its contents

func Parse

func Parse(in io.Reader) (*Set, error)

Parse parses JWK from the incoming io.Reader. This function can handle both single-key and multi-key formats. If you know before hand which format the incoming data is in, you might want to consider using "github.com/lestrrat-go/jwx/internal/json" directly

Note that a successful parsing does NOT guarantee a valid key

func ParseBytes

func ParseBytes(buf []byte) (*Set, error)

ParseBytes parses JWK from the incoming byte buffer.

Note that a successful parsing does NOT guarantee a valid key

func ParseString

func ParseString(s string) (*Set, error)

ParseString parses JWK from the incoming string.

Note that a successful parsing does NOT guarantee a valid key

func (*Set) Iterate added in v1.0.0

func (s *Set) Iterate(ctx context.Context) KeyIterator

func (*Set) Len added in v1.0.0

func (s *Set) Len() int

func (Set) LookupKeyID

func (s Set) LookupKeyID(kid string) []Key

LookupKeyID looks for keys matching the given key id. Note that the Set *may* contain multiple keys with the same key id

func (*Set) UnmarshalJSON

func (s *Set) UnmarshalJSON(data []byte) error

type SymmetricKey

type SymmetricKey interface {
	Key
	FromRaw([]byte) error
	Octets() []byte
}

func NewSymmetricKey added in v1.0.0

func NewSymmetricKey() SymmetricKey

type TargetSnapshot added in v1.0.7

type TargetSnapshot struct {
	URL         string
	NextRefresh time.Time
	LastRefresh time.Time
}

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL