keypairs

package module
v0.6.5 Latest Latest
Warning

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

Go to latest
Published: Oct 21, 2020 License: MIT Imports: 21 Imported by: 1

README

keypairs

JSON Web Key (JWK) support and type safety lightly placed over top of Go's crypto/ecdsa and crypto/rsa

Useful for JWT, JOSE, etc.

key, err := keypairs.ParsePrivateKey(bytesForJWKOrPEMOrDER)

pub, err := keypairs.ParsePublicKey(bytesForJWKOrPEMOrDER)

jwk, err := keypairs.MarshalJWKPublicKey(pub, time.Now().Add(2 * time.Day))

kid, err := keypairs.ThumbprintPublicKey(pub)

GoDoc API Documentation

See https://pkg.go.dev/git.rootprojects.org/root/keypairs

Philosophy

Go's standard library is great.

Go has excellent crytography support and provides wonderful primitives for dealing with them.

I prefer to stay as close to Go's crypto package as possible, just adding a light touch for JWT support and type safety.

Type Safety

crypto.PublicKey is a "marker interface", meaning that it is not typesafe!

go-keypairs defines type keypairs.PrivateKey interface { Public() crypto.PublicKey }, which is implemented by crypto/rsa and crypto/ecdsa (but not crypto/dsa, which we really don't care that much about).

Go1.15 will add [PublicKey.Equal(crypto.PublicKey)](https://github.com/golang/go/issues/21704), which will make it possible to remove the additional wrapper over PublicKey and use an interface instead.

Since there are no common methods between rsa.PublicKey and ecdsa.PublicKey, go-keypairs lightly wraps each to implement Thumbprint() string (part of the JOSE/JWK spec).

JSON Web Key (JWK) as a "codec"

Although there are many, many ways that JWKs could be interpreted (possibly why they haven't made it into the standard library), go-keypairs follows the basic pattern of encoding/x509 to Parse and Marshal only the most basic and most meaningful parts of a key.

I highly recommend that you use Thumbprint() for KeyID you also get the benefit of not losing information when encoding and decoding between the ASN.1, x509, PEM, and JWK formats.

LICENSE

Copyright (c) 2020-present AJ ONeal
Copyright (c) 2018-2019 Big Squid, Inc.

This work is licensed under the terms of the MIT license.
For a copy, see https://opensource.org/licenses/MIT.

Documentation

Overview

Package keypairs complements Go's standard keypair-related packages (encoding/pem, crypto/x509, crypto/rsa, crypto/ecdsa, crypto/elliptic) with JWK encoding support and typesafe PrivateKey and PublicKey interfaces.

Basics

key, err := keypairs.ParsePrivateKey(bytesForJWKOrPEMOrDER)

pub, err := keypairs.ParsePublicKey(bytesForJWKOrPEMOrDER)

jwk, err := keypairs.MarshalJWKPublicKey(pub, time.Now().Add(2 * time.Day))

kid, err := keypairs.ThumbprintPublicKey(pub)

Convenience functions are available which will fetch keys (or retrieve them from cache) via OIDC, .well-known/jwks.json, and direct urls. All keys are cached by Thumbprint, as well as kid(@issuer), if available.

import "git.rootprojects.org/root/keypairs/keyfetch"

pubs, err := keyfetch.OIDCJWKs("https://example.com/")
pubs, err := keyfetch.OIDCJWK(ThumbOrKeyID, "https://example.com/")

pubs, err := keyfetch.WellKnownJWKs("https://example.com/")
pubs, err := keyfetch.WellKnownJWK(ThumbOrKeyID, "https://example.com/")

pubs, err := keyfetch.JWKs("https://example.com/path/to/jwks/")
pubs, err := keyfetch.JWK(ThumbOrKeyID, "https://example.com/path/to/jwks/")

// From URL
pub, err := keyfetch.Fetch("https://example.com/jwk.json")

// From Cache only
pub := keyfetch.Get(thumbprint, "https://example.com/jwk.json")

A non-caching version with the same capabilities is also available.

Index

Constants

View Source
const ErrDevBadKeyType = "" /* 209-byte string literal not displayed */

ErrDevBadKeyType means that the developer compiled bad code that passes the wrong type

View Source
const ErrDevSwapPrivatePublic = "[Developer Error] You passed either crypto.PrivateKey or crypto.PublicKey where the other was expected."

ErrDevSwapPrivatePublic means that the developer compiled bad code that swapped public and private keys

Variables

View Source
var ErrInvalidCurve = errors.New("The JWK's 'crv' must be either of the NIST standards 'P-256' or 'P-384'")

ErrInvalidCurve means that a non-standard curve was used

View Source
var ErrInvalidKeyType = errors.New("The JWK's 'kty' must be either 'RSA' or 'EC'")

ErrInvalidKeyType means that the key is not an acceptable type

View Source
var ErrInvalidPrivateKey = errors.New("PrivateKey must be of type *rsa.PrivateKey or *ecdsa.PrivateKey")

ErrInvalidPrivateKey means that the key is not a valid Private Key

View Source
var ErrInvalidPublicKey = errors.New("PublicKey must be of type *rsa.PublicKey or *ecdsa.PublicKey")

ErrInvalidPublicKey means that the key is not a valid Public Key

View Source
var ErrParseJWK = errors.New("JWK is missing required base64-encoded JSON fields")

ErrParseJWK means that the JWK is valid JSON but not a valid JWK

View Source
var ErrParsePrivateKey = errors.New("PrivateKey bytes could not be parsed as PEM or DER (PKCS8, SEC1, or PKCS1) or JWK")

ErrParsePrivateKey means that the bytes cannot be parsed in any known format

View Source
var ErrParsePublicKey = errors.New("PublicKey bytes could not be parsed as PEM or DER (PKIX/SPKI, PKCS1, or X509 Certificate) or JWK")

ErrParsePublicKey means that the bytes cannot be parsed in any known format

View Source
var ErrUnexpectedPrivateKey = errors.New("PublicKey was given where PrivateKey was expected")

ErrUnexpectedPrivateKey means that a Public Key was expected

View Source
var ErrUnexpectedPublicKey = errors.New("PrivateKey was given where PublicKey was expected")

ErrUnexpectedPublicKey means that a Private Key was expected

Functions

func JWSToJWT added in v0.6.2

func JWSToJWT(jwt *JWS) string

JWSToJWT joins JWS parts into a JWT as {ProtectedHeader}.{SerializedPayload}.{Signature}.

func MarshalDERPrivateKey added in v0.6.0

func MarshalDERPrivateKey(privkey PrivateKey) ([]byte, error)

MarshalDERPrivateKey outputs the given private key as ASN.1 DER

func MarshalDERPublicKey added in v0.6.0

func MarshalDERPublicKey(pubkey crypto.PublicKey) ([]byte, error)

MarshalDERPublicKey outputs the given public key as JWK

func MarshalECPrivateKey added in v0.6.0

func MarshalECPrivateKey(k *ecdsa.PrivateKey) []byte

MarshalECPrivateKey will output the given private key as JWK

func MarshalECPublicKey

func MarshalECPublicKey(k *ecdsa.PublicKey, exp ...time.Time) []byte

MarshalECPublicKey will take an EC key and output a JWK, with optional expiration date

func MarshalECPublicKeyWithoutKeyID

func MarshalECPublicKeyWithoutKeyID(k *ecdsa.PublicKey) []byte

MarshalECPublicKeyWithoutKeyID will output the most minimal version of an EC JWK (no key id, no "use" flag, nada)

func MarshalJWKPrivateKey added in v0.6.0

func MarshalJWKPrivateKey(privkey PrivateKey) []byte

MarshalJWKPrivateKey outputs the given private key as JWK

func MarshalJWKPublicKey

func MarshalJWKPublicKey(key PublicKey, exp ...time.Time) []byte

MarshalJWKPublicKey outputs a JWK with its key id (kid) and an optional expiration, making it suitable for use as an OIDC public key.

func MarshalPEMPrivateKey added in v0.6.0

func MarshalPEMPrivateKey(privkey PrivateKey) ([]byte, error)

MarshalPEMPrivateKey outputs the given private key as ASN.1 PEM

func MarshalPEMPublicKey added in v0.6.0

func MarshalPEMPublicKey(pubkey crypto.PublicKey) ([]byte, error)

MarshalPEMPublicKey outputs the given public key as JWK

func MarshalRSAPrivateKey added in v0.6.0

func MarshalRSAPrivateKey(pk *rsa.PrivateKey) []byte

MarshalRSAPrivateKey will output the given private key as JWK

func MarshalRSAPublicKey

func MarshalRSAPublicKey(p *rsa.PublicKey, exp ...time.Time) []byte

MarshalRSAPublicKey will take an RSA key and output a JWK, with optional expiration date

func MarshalRSAPublicKeyWithoutKeyID

func MarshalRSAPublicKeyWithoutKeyID(p *rsa.PublicKey) []byte

MarshalRSAPublicKeyWithoutKeyID will output the most minimal version of an RSA JWK (no key id, no "use" flag, nada)

func Sign added in v0.6.2

func Sign(privkey PrivateKey, hash []byte, rand io.Reader) []byte

Sign signs both RSA and ECDSA. Use `nil` or `crypto/rand.Reader` except for debugging.

func ThumbprintECPublicKey

func ThumbprintECPublicKey(k *ecdsa.PublicKey) string

ThumbprintECPublicKey will output a RFC-spec SHA256 JWK thumbprint of an EC public key

func ThumbprintPublicKey

func ThumbprintPublicKey(pub PublicKey) string

ThumbprintPublicKey returns the SHA256 RFC-spec JWK thumbprint

func ThumbprintRSAPublicKey

func ThumbprintRSAPublicKey(p *rsa.PublicKey) string

ThumbprintRSAPublicKey will output a RFC-spec SHA256 JWK thumbprint of an EC public key

func ThumbprintUntypedPublicKey

func ThumbprintUntypedPublicKey(pub crypto.PublicKey) string

ThumbprintUntypedPublicKey is a non-typesafe version of ThumbprintPublicKey (but will still panic, to help you discover bugs in development rather than production).

func Verify added in v0.6.3

func Verify(pubkey PublicKey, hash []byte, sig []byte) bool

Verify will check the signature of a hash

func VerifyClaims added in v0.6.3

func VerifyClaims(pubkey PublicKey, jws *JWS) (errs []error)

VerifyClaims will check the signature of a parsed JWT

Types

type ECJWK added in v0.6.3

type ECJWK struct {
	KeyID string   `json:"kid,omitempty"`
	Curve string   `json:"crv"`
	X     string   `json:"x"`
	Y     string   `json:"y"`
	Use   []string `json:"use,omitempty"`
	Seed  string   `json:"_seed,omitempty"`
}

ECJWK is the EC variant

type ECPublicKey

type ECPublicKey struct {
	PublicKey *ecdsa.PublicKey // empty interface
	KID       string
	Expiry    time.Time
}

ECPublicKey adds common methods to *ecdsa.PublicKey for type safety

func (*ECPublicKey) ExpireAt

func (p *ECPublicKey) ExpireAt(t time.Time)

ExpireAt sets the time at which this Public Key should be considered invalid

func (*ECPublicKey) ExpiresAt

func (p *ECPublicKey) ExpiresAt() time.Time

ExpiresAt gets the time at which this Public Key should be considered invalid

func (*ECPublicKey) Key

func (p *ECPublicKey) Key() crypto.PublicKey

Key returns the PublicKey

func (*ECPublicKey) KeyID

func (p *ECPublicKey) KeyID() string

KeyID returns the JWK `kid`, which will be the Thumbprint for keys generated with this library

func (*ECPublicKey) Thumbprint

func (p *ECPublicKey) Thumbprint() string

Thumbprint returns a JWK thumbprint. See https://stackoverflow.com/questions/42588786/how-to-fingerprint-a-jwk

type JWK added in v0.6.3

type JWK interface {
	// contains filtered or unexported methods
}

JWK abstracts EC and RSA keys

type JWS added in v0.6.2

type JWS struct {
	Header    Object `json:"header"`    // JSON
	Claims    Object `json:"claims"`    // JSON
	Protected string `json:"protected"` // base64
	Payload   string `json:"payload"`   // base64
	Signature string `json:"signature"` // base64
}

JWS is a parsed JWT, representation as signable/verifiable and human-readable parts

func JWTToJWS added in v0.6.3

func JWTToJWS(jwt string) (jws *JWS)

JWTToJWS splits the JWT into its JWS segments

func SignClaims added in v0.6.2

func SignClaims(privkey PrivateKey, header Object, claims Object) (*JWS, error)

SignClaims adds `typ`, `kid` (or `jwk`), and `alg` in the header and expects claims for `jti`, `exp`, `iss`, and `iat`

func (*JWS) DecodeComponents added in v0.6.3

func (jws *JWS) DecodeComponents() error

DecodeComponents decodes JWS Header and Claims

type Object added in v0.6.2

type Object = map[string]interface{}

Object is a type alias representing generic JSON data

type PrivateKey

type PrivateKey interface {
	Public() crypto.PublicKey
}

PrivateKey is a zero-cost typesafe substitue for crypto.PrivateKey

func NewDefaultPrivateKey added in v0.6.0

func NewDefaultPrivateKey() PrivateKey

NewDefaultPrivateKey generates a key with reasonable strength. Today that means a 256-bit equivalent - either RSA 2048 or EC P-256.

func ParseJWKPrivateKey

func ParseJWKPrivateKey(b []byte) (PrivateKey, error)

ParseJWKPrivateKey parses a JSON-encoded JWK and returns a PrivateKey, or a (hopefully) helpful error message

func ParsePrivateKey

func ParsePrivateKey(block []byte) (PrivateKey, error)

ParsePrivateKey will try to parse the bytes you give it in any of the supported formats: PEM, DER, PKCS8, PKCS1, SEC1, and JWK

func ParsePrivateKeyString

func ParsePrivateKeyString(block string) (PrivateKey, error)

ParsePrivateKeyString calls ParsePrivateKey([]byte(key)) for all you lazy folk.

type PublicKey

type PublicKey interface {
	crypto.PublicKey
	Thumbprint() string
	KeyID() string
	Key() crypto.PublicKey
	ExpiresAt() time.Time
}

PublicKey thinly veils crypto.PublicKey for type safety

func DecodeJWKPublicKey

func DecodeJWKPublicKey(r io.Reader) (PublicKey, error)

DecodeJWKPublicKey stream-decodes a JSON-encoded JWK and returns a PublicKey, or a (hopefully) helpful error message

func NewJWKPublicKey

func NewJWKPublicKey(m map[string]string) (PublicKey, error)

NewJWKPublicKey contstructs a PublicKey from the relevant pieces a map[string]string (generic JSON)

func NewPublicKey

func NewPublicKey(pub crypto.PublicKey, kid ...string) PublicKey

NewPublicKey wraps a crypto.PublicKey to make it typesafe.

func ParseJWKPublicKey

func ParseJWKPublicKey(b []byte) (PublicKey, error)

ParseJWKPublicKey parses a JSON-encoded JWK and returns a PublicKey, or a (hopefully) helpful error message

func ParseJWKPublicKeyString

func ParseJWKPublicKeyString(s string) (PublicKey, error)

ParseJWKPublicKeyString calls ParseJWKPublicKey([]byte(key)) for all you lazy folk.

func ParsePublicKey

func ParsePublicKey(block []byte) (PublicKey, error)

ParsePublicKey will try to parse the bytes you give it in any of the supported formats: PEM, DER, PKIX/SPKI, PKCS1, x509 Certificate, and JWK

func ParsePublicKeyString

func ParsePublicKeyString(block string) (PublicKey, error)

ParsePublicKeyString calls ParsePublicKey([]byte(key)) for all you lazy folk.

type RSAJWK added in v0.6.3

type RSAJWK struct {
	KeyID string   `json:"kid,omitempty"`
	Exp   string   `json:"e"`
	N     string   `json:"n"`
	Use   []string `json:"use,omitempty"`
	Seed  string   `json:"_seed,omitempty"`
}

RSAJWK is the RSA variant

type RSAPublicKey

type RSAPublicKey struct {
	PublicKey *rsa.PublicKey // empty interface
	KID       string
	Expiry    time.Time
}

RSAPublicKey adds common methods to *rsa.PublicKey for type safety

func (*RSAPublicKey) ExpireAt

func (p *RSAPublicKey) ExpireAt(t time.Time)

ExpireAt sets the time at which this Public Key should be considered invalid

func (*RSAPublicKey) ExpiresAt

func (p *RSAPublicKey) ExpiresAt() time.Time

ExpiresAt gets the time at which this Public Key should be considered invalid

func (*RSAPublicKey) Key

func (p *RSAPublicKey) Key() crypto.PublicKey

Key returns the PublicKey

func (*RSAPublicKey) KeyID

func (p *RSAPublicKey) KeyID() string

KeyID returns the JWK `kid`, which will be the Thumbprint for keys generated with this library

func (*RSAPublicKey) Thumbprint

func (p *RSAPublicKey) Thumbprint() string

Thumbprint returns a JWK thumbprint. See https://stackoverflow.com/questions/42588786/how-to-fingerprint-a-jwk

Directories

Path Synopsis
cmd
Package keyfetch retrieve and cache PublicKeys from OIDC (https://example.com/.well-known/openid-configuration) and Auth0 (https://example.com/.well-known/jwks.json) JWKs URLs and expires them when `exp` is reached (or a default expiry if the key does not provide one).
Package keyfetch retrieve and cache PublicKeys from OIDC (https://example.com/.well-known/openid-configuration) and Auth0 (https://example.com/.well-known/jwks.json) JWKs URLs and expires them when `exp` is reached (or a default expiry if the key does not provide one).
uncached
Package uncached provides uncached versions of go-keypairs/keyfetch
Package uncached provides uncached versions of go-keypairs/keyfetch
Package keyserve provides middleware to serve Public Keys via OIDC-style (https://example.com/.well-known/openid-configuration) and Auth0-style (https://example.com/.well-known/jwks.json) URLs.
Package keyserve provides middleware to serve Public Keys via OIDC-style (https://example.com/.well-known/openid-configuration) and Auth0-style (https://example.com/.well-known/jwks.json) URLs.

Jump to

Keyboard shortcuts

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