keypairs/keypairs.go

646 lines
18 KiB
Go
Raw Normal View History

2019-02-07 01:10:30 +00:00
package keypairs
import (
2019-07-11 16:59:19 +00:00
"bytes"
2019-02-07 01:10:30 +00:00
"crypto"
"crypto/dsa"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
2019-02-08 01:26:45 +00:00
"io"
2019-02-08 23:53:29 +00:00
"log"
2019-02-07 01:10:30 +00:00
"math/big"
2019-07-11 16:59:19 +00:00
"strings"
2019-02-08 23:53:29 +00:00
"time"
2019-02-07 01:10:30 +00:00
)
2020-04-10 17:59:44 +00:00
// ErrInvalidPrivateKey means that the key is not a valid Private Key
2019-03-07 00:16:00 +00:00
var ErrInvalidPrivateKey = errors.New("PrivateKey must be of type *rsa.PrivateKey or *ecdsa.PrivateKey")
2020-04-10 17:59:44 +00:00
// ErrInvalidPublicKey means that the key is not a valid Public Key
2019-03-07 00:16:00 +00:00
var ErrInvalidPublicKey = errors.New("PublicKey must be of type *rsa.PublicKey or *ecdsa.PublicKey")
2020-04-10 17:59:44 +00:00
// ErrParsePublicKey means that the bytes cannot be parsed in any known format
2019-03-07 00:16:00 +00:00
var ErrParsePublicKey = errors.New("PublicKey bytes could not be parsed as PEM or DER (PKIX/SPKI, PKCS1, or X509 Certificate) or JWK")
2020-04-10 17:59:44 +00:00
// ErrParsePrivateKey means that the bytes cannot be parsed in any known format
2019-03-07 00:16:00 +00:00
var ErrParsePrivateKey = errors.New("PrivateKey bytes could not be parsed as PEM or DER (PKCS8, SEC1, or PKCS1) or JWK")
2020-04-10 17:59:44 +00:00
// ErrParseJWK means that the JWK is valid JSON but not a valid JWK
2019-03-07 00:16:00 +00:00
var ErrParseJWK = errors.New("JWK is missing required base64-encoded JSON fields")
2020-04-10 17:59:44 +00:00
// ErrInvalidKeyType means that the key is not an acceptable type
2019-03-07 00:16:00 +00:00
var ErrInvalidKeyType = errors.New("The JWK's 'kty' must be either 'RSA' or 'EC'")
2020-04-10 17:59:44 +00:00
// ErrInvalidCurve means that a non-standard curve was used
2019-03-07 00:16:00 +00:00
var ErrInvalidCurve = errors.New("The JWK's 'crv' must be either of the NIST standards 'P-256' or 'P-384'")
2020-04-10 17:59:44 +00:00
// ErrUnexpectedPublicKey means that a Private Key was expected
2019-07-11 16:59:19 +00:00
var ErrUnexpectedPublicKey = errors.New("PrivateKey was given where PublicKey was expected")
2020-04-10 17:59:44 +00:00
// ErrUnexpectedPrivateKey means that a Public Key was expected
2019-07-11 16:59:19 +00:00
var ErrUnexpectedPrivateKey = errors.New("PublicKey was given where PrivateKey was expected")
2019-02-07 01:31:48 +00:00
2020-04-10 17:59:44 +00:00
// ErrDevSwapPrivatePublic means that the developer compiled bad code that swapped public and private keys
2019-03-07 00:16:00 +00:00
const ErrDevSwapPrivatePublic = "[Developer Error] You passed either crypto.PrivateKey or crypto.PublicKey where the other was expected."
2019-02-08 23:53:29 +00:00
2020-04-10 17:59:44 +00:00
// ErrDevBadKeyType means that the developer compiled bad code that passes the wrong type
2019-03-07 00:16:00 +00:00
const ErrDevBadKeyType = "[Developer Error] crypto.PublicKey and crypto.PrivateKey are somewhat deceptive. They're actually empty interfaces that accept any object, even non-crypto objects. You passed an object of type '%T' by mistake."
2019-02-08 23:53:29 +00:00
// PrivateKey is a zero-cost typesafe substitue for crypto.PrivateKey
2019-02-07 01:10:30 +00:00
type PrivateKey interface {
Public() crypto.PublicKey
}
2019-02-08 23:53:29 +00:00
// PublicKey thinly veils crypto.PublicKey for type safety
type PublicKey interface {
crypto.PublicKey
Thumbprint() string
2019-02-20 19:26:37 +00:00
KeyID() string
2019-02-08 23:53:29 +00:00
Key() crypto.PublicKey
2019-02-28 22:31:10 +00:00
ExpiresAt() time.Time
2019-02-08 23:53:29 +00:00
}
2019-03-07 00:16:00 +00:00
// ECPublicKey adds common methods to *ecdsa.PublicKey for type safety
2019-02-08 23:53:29 +00:00
type ECPublicKey struct {
PublicKey *ecdsa.PublicKey // empty interface
KID string
Expiry time.Time
}
2019-03-07 00:16:00 +00:00
// RSAPublicKey adds common methods to *rsa.PublicKey for type safety
2019-02-08 23:53:29 +00:00
type RSAPublicKey struct {
PublicKey *rsa.PublicKey // empty interface
KID string
Expiry time.Time
}
2020-04-10 17:59:44 +00:00
// Thumbprint returns a JWK thumbprint. See https://stackoverflow.com/questions/42588786/how-to-fingerprint-a-jwk
2019-02-08 23:53:29 +00:00
func (p *ECPublicKey) Thumbprint() string {
return ThumbprintUntypedPublicKey(p.PublicKey)
}
2020-04-10 17:59:44 +00:00
// KeyID returns the JWK `kid`, which will be the Thumbprint for keys generated with this library
2019-02-20 19:26:37 +00:00
func (p *ECPublicKey) KeyID() string {
return p.KID
}
2020-04-10 17:59:44 +00:00
// Key returns the PublicKey
2019-02-08 23:53:29 +00:00
func (p *ECPublicKey) Key() crypto.PublicKey {
return p.PublicKey
}
2020-04-10 17:59:44 +00:00
// ExpireAt sets the time at which this Public Key should be considered invalid
2019-02-09 00:43:50 +00:00
func (p *ECPublicKey) ExpireAt(t time.Time) {
p.Expiry = t
}
2020-04-10 17:59:44 +00:00
// ExpiresAt gets the time at which this Public Key should be considered invalid
2019-02-28 22:31:10 +00:00
func (p *ECPublicKey) ExpiresAt() time.Time {
return p.Expiry
}
2019-02-09 00:43:50 +00:00
2020-04-10 17:59:44 +00:00
// Thumbprint returns a JWK thumbprint. See https://stackoverflow.com/questions/42588786/how-to-fingerprint-a-jwk
2019-02-08 23:53:29 +00:00
func (p *RSAPublicKey) Thumbprint() string {
return ThumbprintUntypedPublicKey(p.PublicKey)
}
2020-04-10 17:59:44 +00:00
// KeyID returns the JWK `kid`, which will be the Thumbprint for keys generated with this library
2019-02-20 19:26:37 +00:00
func (p *RSAPublicKey) KeyID() string {
return p.KID
}
2020-04-10 17:59:44 +00:00
// Key returns the PublicKey
2019-02-08 23:53:29 +00:00
func (p *RSAPublicKey) Key() crypto.PublicKey {
return p.PublicKey
}
2020-04-10 17:59:44 +00:00
// ExpireAt sets the time at which this Public Key should be considered invalid
2019-02-09 00:43:50 +00:00
func (p *RSAPublicKey) ExpireAt(t time.Time) {
p.Expiry = t
}
2020-04-10 17:59:44 +00:00
// ExpiresAt gets the time at which this Public Key should be considered invalid
2019-02-28 22:31:10 +00:00
func (p *RSAPublicKey) ExpiresAt() time.Time {
return p.Expiry
}
2019-02-08 23:53:29 +00:00
2019-02-21 00:10:42 +00:00
// NewPublicKey wraps a crypto.PublicKey to make it typesafe.
2019-02-09 00:43:50 +00:00
func NewPublicKey(pub crypto.PublicKey, kid ...string) PublicKey {
2019-02-08 23:53:29 +00:00
var k PublicKey
switch p := pub.(type) {
case *ecdsa.PublicKey:
eckey := &ECPublicKey{
PublicKey: p,
}
if 0 != len(kid) {
eckey.KID = kid[0]
} else {
2019-02-09 00:50:56 +00:00
eckey.KID = ThumbprintECPublicKey(p)
2019-02-08 23:53:29 +00:00
}
k = eckey
case *rsa.PublicKey:
rsakey := &RSAPublicKey{
PublicKey: p,
}
if 0 != len(kid) {
rsakey.KID = kid[0]
} else {
2019-02-09 00:50:56 +00:00
rsakey.KID = ThumbprintRSAPublicKey(p)
2019-02-08 23:53:29 +00:00
}
k = rsakey
case *ecdsa.PrivateKey:
2019-03-07 00:16:00 +00:00
panic(errors.New(ErrDevSwapPrivatePublic))
2019-02-08 23:53:29 +00:00
case *rsa.PrivateKey:
2019-03-07 00:16:00 +00:00
panic(errors.New(ErrDevSwapPrivatePublic))
2019-02-08 23:53:29 +00:00
case *dsa.PublicKey:
2019-03-07 00:16:00 +00:00
panic(ErrInvalidPublicKey)
2019-02-08 23:53:29 +00:00
case *dsa.PrivateKey:
2020-04-10 17:59:44 +00:00
panic(ErrInvalidPrivateKey)
2019-02-08 23:53:29 +00:00
default:
2020-04-10 17:59:44 +00:00
panic(fmt.Errorf(ErrDevBadKeyType, pub))
2019-02-08 23:53:29 +00:00
}
return k
}
2019-03-07 00:16:00 +00:00
// MarshalJWKPublicKey outputs a JWK with its key id (kid) and an optional expiration,
// making it suitable for use as an OIDC public key.
2019-02-09 00:43:50 +00:00
func MarshalJWKPublicKey(key PublicKey, exp ...time.Time) []byte {
2019-02-07 01:10:30 +00:00
// thumbprint keys are alphabetically sorted and only include the necessary public parts
2019-02-08 23:53:29 +00:00
switch k := key.Key().(type) {
2019-02-07 01:10:30 +00:00
case *rsa.PublicKey:
2019-02-09 00:43:50 +00:00
return MarshalRSAPublicKey(k, exp...)
2019-02-07 01:10:30 +00:00
case *ecdsa.PublicKey:
2019-02-09 00:43:50 +00:00
return MarshalECPublicKey(k, exp...)
2019-02-07 01:10:30 +00:00
case *dsa.PublicKey:
2019-03-07 00:16:00 +00:00
panic(ErrInvalidPublicKey)
2019-02-07 01:10:30 +00:00
default:
// this is unreachable because we know the types that we pass in
2019-02-08 23:53:29 +00:00
log.Printf("keytype: %t, %+v\n", key, key)
2019-03-07 00:16:00 +00:00
panic(ErrInvalidPublicKey)
2019-02-07 01:10:30 +00:00
}
}
2019-03-07 00:16:00 +00:00
// ThumbprintPublicKey returns the SHA256 RFC-spec JWK thumbprint
func ThumbprintPublicKey(pub PublicKey) string {
return ThumbprintUntypedPublicKey(pub.Key())
2019-02-08 23:53:29 +00:00
}
2019-03-07 00:16:00 +00:00
// ThumbprintUntypedPublicKey is a non-typesafe version of ThumbprintPublicKey
// (but will still panic, to help you discover bugs in development rather than production).
2019-02-08 23:53:29 +00:00
func ThumbprintUntypedPublicKey(pub crypto.PublicKey) string {
2019-02-07 20:05:26 +00:00
switch p := pub.(type) {
2019-03-07 00:16:00 +00:00
case PublicKey:
return ThumbprintUntypedPublicKey(p.Key())
2019-02-07 20:05:26 +00:00
case *ecdsa.PublicKey:
return ThumbprintECPublicKey(p)
case *rsa.PublicKey:
return ThumbprintRSAPublicKey(p)
default:
2019-03-07 00:16:00 +00:00
panic(ErrInvalidPublicKey)
2019-02-07 20:05:26 +00:00
}
}
2019-03-07 00:16:00 +00:00
// MarshalECPublicKey will take an EC key and output a JWK, with optional expiration date
2019-02-09 00:43:50 +00:00
func MarshalECPublicKey(k *ecdsa.PublicKey, exp ...time.Time) []byte {
2019-02-07 21:19:37 +00:00
thumb := ThumbprintECPublicKey(k)
2019-02-07 20:05:26 +00:00
crv := k.Curve.Params().Name
2019-02-07 01:31:48 +00:00
x := base64.RawURLEncoding.EncodeToString(k.X.Bytes())
y := base64.RawURLEncoding.EncodeToString(k.Y.Bytes())
2019-02-09 00:43:50 +00:00
expstr := ""
if 0 != len(exp) {
2019-02-11 22:38:40 +00:00
expstr = fmt.Sprintf(`"exp":%d,`, exp[0].Unix())
2019-02-09 00:43:50 +00:00
}
2019-02-28 00:54:01 +00:00
return []byte(fmt.Sprintf(`{"kid":%q,"use":"sig",%s"crv":%q,"kty":"EC","x":%q,"y":%q}`, thumb, expstr, crv, x, y))
2019-02-07 19:30:04 +00:00
}
2019-03-07 00:16:00 +00:00
// MarshalECPublicKeyWithoutKeyID will output the most minimal version of an EC JWK (no key id, no "use" flag, nada)
2019-02-07 21:19:37 +00:00
func MarshalECPublicKeyWithoutKeyID(k *ecdsa.PublicKey) []byte {
2019-02-07 20:05:26 +00:00
crv := k.Curve.Params().Name
2019-02-07 19:30:04 +00:00
x := base64.RawURLEncoding.EncodeToString(k.X.Bytes())
y := base64.RawURLEncoding.EncodeToString(k.Y.Bytes())
2019-02-07 21:19:37 +00:00
return []byte(fmt.Sprintf(`{"crv":%q,"kty":"EC","x":%q,"y":%q}`, crv, x, y))
}
2019-03-07 00:16:00 +00:00
// ThumbprintECPublicKey will output a RFC-spec SHA256 JWK thumbprint of an EC public key
2019-02-07 21:19:37 +00:00
func ThumbprintECPublicKey(k *ecdsa.PublicKey) string {
thumbprintable := MarshalECPublicKeyWithoutKeyID(k)
2019-02-07 19:30:04 +00:00
sha := sha256.Sum256(thumbprintable)
return base64.RawURLEncoding.EncodeToString(sha[:])
2019-02-07 01:31:48 +00:00
}
2019-03-07 00:16:00 +00:00
// MarshalRSAPublicKey will take an RSA key and output a JWK, with optional expiration date
2019-02-09 00:43:50 +00:00
func MarshalRSAPublicKey(p *rsa.PublicKey, exp ...time.Time) []byte {
2019-02-07 21:19:37 +00:00
thumb := ThumbprintRSAPublicKey(p)
2019-02-07 20:05:26 +00:00
e := base64.RawURLEncoding.EncodeToString(big.NewInt(int64(p.E)).Bytes())
n := base64.RawURLEncoding.EncodeToString(p.N.Bytes())
2019-02-09 00:43:50 +00:00
expstr := ""
if 0 != len(exp) {
2019-02-11 22:38:40 +00:00
expstr = fmt.Sprintf(`"exp":%d,`, exp[0].Unix())
2019-02-09 00:43:50 +00:00
}
2019-02-28 00:54:01 +00:00
return []byte(fmt.Sprintf(`{"kid":%q,"use":"sig",%s"e":%q,"kty":"RSA","n":%q}`, thumb, expstr, e, n))
2019-02-07 20:05:26 +00:00
}
2019-03-07 00:16:00 +00:00
// MarshalRSAPublicKeyWithoutKeyID will output the most minimal version of an RSA JWK (no key id, no "use" flag, nada)
2019-02-07 21:19:37 +00:00
func MarshalRSAPublicKeyWithoutKeyID(p *rsa.PublicKey) []byte {
2019-02-07 20:05:26 +00:00
e := base64.RawURLEncoding.EncodeToString(big.NewInt(int64(p.E)).Bytes())
n := base64.RawURLEncoding.EncodeToString(p.N.Bytes())
2019-02-07 21:19:37 +00:00
return []byte(fmt.Sprintf(`{"e":%q,"kty":"RSA","n":%q}`, e, n))
}
2019-03-07 00:16:00 +00:00
// ThumbprintRSAPublicKey will output a RFC-spec SHA256 JWK thumbprint of an EC public key
2019-02-07 21:19:37 +00:00
func ThumbprintRSAPublicKey(p *rsa.PublicKey) string {
thumbprintable := MarshalRSAPublicKeyWithoutKeyID(p)
2019-02-07 01:31:48 +00:00
sha := sha256.Sum256([]byte(thumbprintable))
2019-02-07 20:05:26 +00:00
return base64.RawURLEncoding.EncodeToString(sha[:])
2019-02-07 01:31:48 +00:00
}
2019-02-07 01:10:30 +00:00
2019-03-07 00:16:00 +00:00
// ParsePrivateKey will try to parse the bytes you give it
// in any of the supported formats: PEM, DER, PKCS8, PKCS1, SEC1, and JWK
2019-02-07 19:30:04 +00:00
func ParsePrivateKey(block []byte) (PrivateKey, error) {
2019-03-07 00:16:00 +00:00
blocks, err := getPEMBytes(block)
if nil != err {
return nil, ErrParsePrivateKey
2019-02-07 01:10:30 +00:00
}
// Parse PEM blocks (openssl generates junk metadata blocks for ECs)
// or the original DER, or the JWK
2020-04-10 17:59:44 +00:00
for i := range blocks {
2019-02-07 01:10:30 +00:00
block = blocks[i]
2019-02-07 19:30:04 +00:00
if key, err := parsePrivateKey(block); nil == err {
return key, nil
2019-02-07 01:10:30 +00:00
}
}
2019-07-11 16:59:19 +00:00
for i := range blocks {
block = blocks[i]
if _, err := parsePublicKey(block); nil == err {
return nil, ErrUnexpectedPublicKey
}
}
2019-02-07 01:10:30 +00:00
// If we didn't parse a key arleady, we failed
2019-03-07 00:16:00 +00:00
return nil, ErrParsePrivateKey
2019-02-07 01:10:30 +00:00
}
2019-03-07 00:16:00 +00:00
// ParsePrivateKeyString calls ParsePrivateKey([]byte(key)) for all you lazy folk.
2019-02-08 23:53:29 +00:00
func ParsePrivateKeyString(block string) (PrivateKey, error) {
return ParsePrivateKey([]byte(block))
}
2019-02-07 19:30:04 +00:00
func parsePrivateKey(der []byte) (PrivateKey, error) {
var key PrivateKey
2019-02-07 20:05:26 +00:00
//fmt.Println("1. ParsePKCS8PrivateKey")
2019-02-07 19:30:04 +00:00
xkey, err := x509.ParsePKCS8PrivateKey(der)
if nil == err {
switch k := xkey.(type) {
case *rsa.PrivateKey:
key = k
case *ecdsa.PrivateKey:
key = k
default:
2019-03-07 00:16:00 +00:00
err = errors.New("Only RSA and ECDSA (EC) Private Keys are supported")
2019-02-07 19:30:04 +00:00
}
2019-02-07 01:10:30 +00:00
}
2019-02-07 19:30:04 +00:00
if nil != err {
2019-02-07 20:05:26 +00:00
//fmt.Println("2. ParseECPrivateKey")
2019-02-07 19:30:04 +00:00
key, err = x509.ParseECPrivateKey(der)
if nil != err {
2019-02-07 20:05:26 +00:00
//fmt.Println("3. ParsePKCS1PrivateKey")
2019-02-07 19:30:04 +00:00
key, err = x509.ParsePKCS1PrivateKey(der)
if nil != err {
2019-02-07 20:05:26 +00:00
//fmt.Println("4. ParseJWKPrivateKey")
2019-02-07 19:30:04 +00:00
key, err = ParseJWKPrivateKey(der)
2019-02-07 01:10:30 +00:00
}
}
}
2019-02-07 19:30:04 +00:00
// But did you know?
// You must return nil explicitly for interfaces
// https://golang.org/doc/faq#nil_error
if nil != err {
return nil, err
}
return key, nil
2019-02-07 01:10:30 +00:00
}
2019-03-07 00:16:00 +00:00
func getPEMBytes(block []byte) ([][]byte, error) {
var pemblock *pem.Block
var blocks = make([][]byte, 0, 1)
// Parse the PEM, if it's a pem
for {
pemblock, block = pem.Decode(block)
if nil != pemblock {
// got one block, there may be more
blocks = append(blocks, pemblock.Bytes)
} else {
// the last block was not a PEM block
// therefore the next isn't either
if 0 != len(block) {
blocks = append(blocks, block)
}
break
}
}
if len(blocks) > 0 {
return blocks, nil
}
2020-04-10 17:59:44 +00:00
return nil, errors.New("no PEM blocks found")
2019-03-07 00:16:00 +00:00
}
2019-03-15 23:52:53 +00:00
// ParsePublicKey will try to parse the bytes you give it
2019-03-07 00:16:00 +00:00
// in any of the supported formats: PEM, DER, PKIX/SPKI, PKCS1, x509 Certificate, and JWK
func ParsePublicKey(block []byte) (PublicKey, error) {
blocks, err := getPEMBytes(block)
if nil != err {
return nil, ErrParsePublicKey
}
// Parse PEM blocks (openssl generates junk metadata blocks for ECs)
// or the original DER, or the JWK
2019-03-15 23:52:53 +00:00
for i := range blocks {
2019-03-07 00:16:00 +00:00
block = blocks[i]
if key, err := parsePublicKey(block); nil == err {
return key, nil
}
}
2019-07-11 16:59:19 +00:00
for i := range blocks {
block = blocks[i]
if _, err := parsePrivateKey(block); nil == err {
return nil, ErrUnexpectedPrivateKey
}
}
2019-03-07 00:16:00 +00:00
// If we didn't parse a key arleady, we failed
return nil, ErrParsePublicKey
}
// ParsePublicKeyString calls ParsePublicKey([]byte(key)) for all you lazy folk.
func ParsePublicKeyString(block string) (PublicKey, error) {
return ParsePublicKey([]byte(block))
}
func parsePublicKey(der []byte) (PublicKey, error) {
cert, err := x509.ParseCertificate(der)
if nil == err {
switch k := cert.PublicKey.(type) {
case *rsa.PublicKey:
return NewPublicKey(k), nil
case *ecdsa.PublicKey:
return NewPublicKey(k), nil
default:
2019-03-15 23:52:53 +00:00
return nil, errors.New("Only RSA and ECDSA (EC) Public Keys are supported")
2019-03-07 00:16:00 +00:00
}
}
//fmt.Println("1. ParsePKIXPublicKey")
xkey, err := x509.ParsePKIXPublicKey(der)
if nil == err {
switch k := xkey.(type) {
case *rsa.PublicKey:
return NewPublicKey(k), nil
case *ecdsa.PublicKey:
return NewPublicKey(k), nil
default:
2019-03-15 23:52:53 +00:00
return nil, errors.New("Only RSA and ECDSA (EC) Public Keys are supported")
2019-03-07 00:16:00 +00:00
}
}
2019-03-15 23:52:53 +00:00
//fmt.Println("3. ParsePKCS1PrublicKey")
rkey, err := x509.ParsePKCS1PublicKey(der)
if nil == err {
//fmt.Println("4. ParseJWKPublicKey")
return NewPublicKey(rkey), nil
2019-03-07 00:16:00 +00:00
}
2019-03-15 23:52:53 +00:00
return ParseJWKPublicKey(der)
2019-03-07 00:16:00 +00:00
2019-03-15 23:52:53 +00:00
/*
// But did you know?
// You must return nil explicitly for interfaces
// https://golang.org/doc/faq#nil_error
if nil != err {
return nil, err
}
*/
2019-03-07 00:16:00 +00:00
}
// NewJWKPublicKey contstructs a PublicKey from the relevant pieces a map[string]string (generic JSON)
2019-02-08 23:53:29 +00:00
func NewJWKPublicKey(m map[string]string) (PublicKey, error) {
2019-02-07 01:31:48 +00:00
switch m["kty"] {
case "RSA":
2019-02-07 19:30:04 +00:00
return parseRSAPublicKey(m)
2019-02-07 01:31:48 +00:00
case "EC":
2019-02-07 19:30:04 +00:00
return parseECPublicKey(m)
2019-02-07 01:31:48 +00:00
default:
2019-03-07 00:16:00 +00:00
return nil, ErrInvalidKeyType
2019-02-08 01:26:45 +00:00
}
}
2019-03-07 00:16:00 +00:00
// ParseJWKPublicKey parses a JSON-encoded JWK and returns a PublicKey, or a (hopefully) helpful error message
2019-02-08 23:53:29 +00:00
func ParseJWKPublicKey(b []byte) (PublicKey, error) {
2019-07-11 16:59:19 +00:00
// RSA and EC have "d" as a private part
if bytes.Contains(b, []byte(`"d"`)) {
return nil, ErrUnexpectedPrivateKey
}
2019-02-08 01:26:45 +00:00
return newJWKPublicKey(b)
}
2019-03-07 00:16:00 +00:00
// ParseJWKPublicKeyString calls ParseJWKPublicKey([]byte(key)) for all you lazy folk.
2019-02-08 23:53:29 +00:00
func ParseJWKPublicKeyString(s string) (PublicKey, error) {
2019-07-11 16:59:19 +00:00
if strings.Contains(s, `"d"`) {
return nil, ErrUnexpectedPrivateKey
}
2019-02-08 01:26:45 +00:00
return newJWKPublicKey(s)
}
2019-03-07 00:16:00 +00:00
// DecodeJWKPublicKey stream-decodes a JSON-encoded JWK and returns a PublicKey, or a (hopefully) helpful error message
2019-02-08 23:53:29 +00:00
func DecodeJWKPublicKey(r io.Reader) (PublicKey, error) {
2019-07-11 16:59:19 +00:00
m := make(map[string]string)
if err := json.NewDecoder(r).Decode(&m); nil != err {
return nil, err
}
if d := m["d"]; "" != d {
return nil, ErrUnexpectedPrivateKey
}
return newJWKPublicKey(m)
2019-02-08 01:26:45 +00:00
}
2019-03-07 00:16:00 +00:00
// the underpinnings of the parser as used by the typesafe wrappers
2019-02-08 23:53:29 +00:00
func newJWKPublicKey(data interface{}) (PublicKey, error) {
2019-02-08 01:26:45 +00:00
var m map[string]string
switch d := data.(type) {
case map[string]string:
m = d
case string:
if err := json.Unmarshal([]byte(d), &m); nil != err {
return nil, err
}
case []byte:
if err := json.Unmarshal(d, &m); nil != err {
return nil, err
}
default:
panic("Developer Error: unsupported interface type")
2019-02-07 01:31:48 +00:00
}
2019-02-08 01:26:45 +00:00
return NewJWKPublicKey(m)
2019-02-07 01:31:48 +00:00
}
2019-03-07 00:16:00 +00:00
// ParseJWKPrivateKey parses a JSON-encoded JWK and returns a PrivateKey, or a (hopefully) helpful error message
2019-02-07 19:30:04 +00:00
func ParseJWKPrivateKey(b []byte) (PrivateKey, error) {
2019-02-07 01:10:30 +00:00
var m map[string]string
2019-02-07 19:30:04 +00:00
if err := json.Unmarshal(b, &m); nil != err {
return nil, err
2019-02-07 01:10:30 +00:00
}
switch m["kty"] {
case "RSA":
2019-02-07 19:30:04 +00:00
return parseRSAPrivateKey(m)
2019-02-07 01:10:30 +00:00
case "EC":
2019-02-07 19:30:04 +00:00
return parseECPrivateKey(m)
2019-02-07 01:10:30 +00:00
default:
2019-03-07 00:16:00 +00:00
return nil, ErrInvalidKeyType
2019-02-07 01:10:30 +00:00
}
}
2019-02-08 23:53:29 +00:00
func parseRSAPublicKey(m map[string]string) (*RSAPublicKey, error) {
// TODO grab expiry?
kid, _ := m["kid"]
2019-02-07 01:10:30 +00:00
n, _ := base64.RawURLEncoding.DecodeString(m["n"])
e, _ := base64.RawURLEncoding.DecodeString(m["e"])
if 0 == len(n) || 0 == len(e) {
2019-03-07 00:16:00 +00:00
return nil, ErrParseJWK
2019-02-07 01:10:30 +00:00
}
ni := &big.Int{}
ni.SetBytes(n)
ei := &big.Int{}
ei.SetBytes(e)
2019-02-08 23:53:29 +00:00
pub := &rsa.PublicKey{
2019-02-07 01:10:30 +00:00
N: ni,
E: int(ei.Int64()),
}
2019-02-08 23:53:29 +00:00
return &RSAPublicKey{
PublicKey: pub,
KID: kid,
}, nil
2019-02-07 01:31:48 +00:00
}
2019-02-07 19:30:04 +00:00
func parseRSAPrivateKey(m map[string]string) (key *rsa.PrivateKey, err error) {
2019-02-08 23:53:29 +00:00
pub, err := parseRSAPublicKey(m)
2019-02-07 01:31:48 +00:00
if nil != err {
return
}
2019-02-07 01:10:30 +00:00
d, _ := base64.RawURLEncoding.DecodeString(m["d"])
p, _ := base64.RawURLEncoding.DecodeString(m["p"])
q, _ := base64.RawURLEncoding.DecodeString(m["q"])
dp, _ := base64.RawURLEncoding.DecodeString(m["dp"])
dq, _ := base64.RawURLEncoding.DecodeString(m["dq"])
qinv, _ := base64.RawURLEncoding.DecodeString(m["qi"])
if 0 == len(d) || 0 == len(p) || 0 == len(dp) || 0 == len(dq) || 0 == len(qinv) {
2019-03-07 00:16:00 +00:00
return nil, ErrParseJWK
2019-02-07 01:10:30 +00:00
}
di := &big.Int{}
di.SetBytes(d)
pi := &big.Int{}
pi.SetBytes(p)
qi := &big.Int{}
qi.SetBytes(q)
dpi := &big.Int{}
dpi.SetBytes(dp)
dqi := &big.Int{}
dqi.SetBytes(dq)
qinvi := &big.Int{}
qinvi.SetBytes(qinv)
key = &rsa.PrivateKey{
2019-02-08 23:53:29 +00:00
PublicKey: *pub.PublicKey,
2019-02-07 01:10:30 +00:00
D: di,
Primes: []*big.Int{pi, qi},
Precomputed: rsa.PrecomputedValues{
Dp: dpi,
Dq: dqi,
Qinv: qinvi,
},
}
return
}
2019-02-08 23:53:29 +00:00
func parseECPublicKey(m map[string]string) (*ECPublicKey, error) {
// TODO grab expiry?
kid, _ := m["kid"]
2019-02-07 19:30:04 +00:00
x, _ := base64.RawURLEncoding.DecodeString(m["x"])
y, _ := base64.RawURLEncoding.DecodeString(m["y"])
2019-02-07 01:10:30 +00:00
if 0 == len(x) || 0 == len(y) || 0 == len(m["crv"]) {
2019-03-07 00:16:00 +00:00
return nil, ErrParseJWK
2019-02-07 01:10:30 +00:00
}
xi := &big.Int{}
xi.SetBytes(x)
yi := &big.Int{}
yi.SetBytes(y)
var crv elliptic.Curve
switch m["crv"] {
case "P-256":
crv = elliptic.P256()
case "P-384":
crv = elliptic.P384()
case "P-521":
crv = elliptic.P521()
default:
2019-03-07 00:16:00 +00:00
return nil, ErrInvalidCurve
2019-02-07 01:10:30 +00:00
}
2019-02-08 23:53:29 +00:00
pub := &ecdsa.PublicKey{
2019-02-07 01:10:30 +00:00
Curve: crv,
X: xi,
Y: yi,
}
2019-02-08 23:53:29 +00:00
return &ECPublicKey{
PublicKey: pub,
KID: kid,
}, nil
2019-02-07 01:31:48 +00:00
}
2019-02-07 19:30:04 +00:00
func parseECPrivateKey(m map[string]string) (*ecdsa.PrivateKey, error) {
pub, err := parseECPublicKey(m)
2019-02-07 01:31:48 +00:00
if nil != err {
2019-02-07 19:30:04 +00:00
return nil, err
2019-02-07 01:31:48 +00:00
}
2019-02-07 01:10:30 +00:00
d, _ := base64.RawURLEncoding.DecodeString(m["d"])
if 0 == len(d) {
2019-03-07 00:16:00 +00:00
return nil, ErrParseJWK
2019-02-07 01:10:30 +00:00
}
di := &big.Int{}
di.SetBytes(d)
2019-02-07 19:30:04 +00:00
return &ecdsa.PrivateKey{
2019-02-08 23:53:29 +00:00
PublicKey: *pub.PublicKey,
2019-02-07 01:10:30 +00:00
D: di,
2019-02-07 19:30:04 +00:00
}, nil
2019-02-07 01:10:30 +00:00
}