parse certificate files, and golint
This commit is contained in:
parent
d823672eb4
commit
1028c3baa5
200
keypairs.go
200
keypairs.go
|
@ -19,16 +19,17 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var EInvalidPrivateKey = errors.New("PrivateKey must be of type *rsa.PrivateKey or *ecdsa.PrivateKey")
|
var ErrInvalidPrivateKey = errors.New("PrivateKey must be of type *rsa.PrivateKey or *ecdsa.PrivateKey")
|
||||||
var EInvalidPublicKey = errors.New("PublicKey must be of type *rsa.PublicKey or *ecdsa.PublicKey")
|
var ErrInvalidPublicKey = errors.New("PublicKey must be of type *rsa.PublicKey or *ecdsa.PublicKey")
|
||||||
var EParsePrivateKey = errors.New("PrivateKey bytes could not be parsed as PEM or DER (PKCS8, SEC1, or PKCS1) or JWK")
|
var ErrParsePublicKey = errors.New("PublicKey bytes could not be parsed as PEM or DER (PKIX/SPKI, PKCS1, or X509 Certificate) or JWK")
|
||||||
var EParseJWK = errors.New("JWK is missing required base64-encoded JSON fields")
|
var ErrParsePrivateKey = errors.New("PrivateKey bytes could not be parsed as PEM or DER (PKCS8, SEC1, or PKCS1) or JWK")
|
||||||
var EInvalidKeyType = errors.New("The JWK's 'kty' must be either 'RSA' or 'EC'")
|
var ErrParseJWK = errors.New("JWK is missing required base64-encoded JSON fields")
|
||||||
var EInvalidCurve = errors.New("The JWK's 'crv' must be either of the NIST standards 'P-256' or 'P-384'")
|
var ErrInvalidKeyType = errors.New("The JWK's 'kty' must be either 'RSA' or 'EC'")
|
||||||
|
var ErrInvalidCurve = errors.New("The JWK's 'crv' must be either of the NIST standards 'P-256' or 'P-384'")
|
||||||
|
|
||||||
const EDevSwapPrivatePublic = "[Developer Error] You passed either crypto.PrivateKey or crypto.PublicKey where the other was expected."
|
const ErrDevSwapPrivatePublic = "[Developer Error] You passed either crypto.PrivateKey or crypto.PublicKey where the other was expected."
|
||||||
|
|
||||||
const EDevBadKeyType = "[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."
|
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."
|
||||||
|
|
||||||
// PrivateKey is a zero-cost typesafe substitue for crypto.PrivateKey
|
// PrivateKey is a zero-cost typesafe substitue for crypto.PrivateKey
|
||||||
type PrivateKey interface {
|
type PrivateKey interface {
|
||||||
|
@ -44,12 +45,14 @@ type PublicKey interface {
|
||||||
ExpiresAt() time.Time
|
ExpiresAt() time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ECPublicKey adds common methods to *ecdsa.PublicKey for type safety
|
||||||
type ECPublicKey struct {
|
type ECPublicKey struct {
|
||||||
PublicKey *ecdsa.PublicKey // empty interface
|
PublicKey *ecdsa.PublicKey // empty interface
|
||||||
KID string
|
KID string
|
||||||
Expiry time.Time
|
Expiry time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RSAPublicKey adds common methods to *rsa.PublicKey for type safety
|
||||||
type RSAPublicKey struct {
|
type RSAPublicKey struct {
|
||||||
PublicKey *rsa.PublicKey // empty interface
|
PublicKey *rsa.PublicKey // empty interface
|
||||||
KID string
|
KID string
|
||||||
|
@ -113,20 +116,22 @@ func NewPublicKey(pub crypto.PublicKey, kid ...string) PublicKey {
|
||||||
}
|
}
|
||||||
k = rsakey
|
k = rsakey
|
||||||
case *ecdsa.PrivateKey:
|
case *ecdsa.PrivateKey:
|
||||||
panic(errors.New(EDevSwapPrivatePublic))
|
panic(errors.New(ErrDevSwapPrivatePublic))
|
||||||
case *rsa.PrivateKey:
|
case *rsa.PrivateKey:
|
||||||
panic(errors.New(EDevSwapPrivatePublic))
|
panic(errors.New(ErrDevSwapPrivatePublic))
|
||||||
case *dsa.PublicKey:
|
case *dsa.PublicKey:
|
||||||
panic(EInvalidPublicKey)
|
panic(ErrInvalidPublicKey)
|
||||||
case *dsa.PrivateKey:
|
case *dsa.PrivateKey:
|
||||||
panic(EInvalidPublicKey)
|
panic(ErrInvalidPublicKey)
|
||||||
default:
|
default:
|
||||||
panic(errors.New(fmt.Sprintf(EDevBadKeyType, pub)))
|
panic(errors.New(fmt.Sprintf(ErrDevBadKeyType, pub)))
|
||||||
}
|
}
|
||||||
|
|
||||||
return k
|
return k
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalJWKPublicKey outputs a JWK with its key id (kid) and an optional expiration,
|
||||||
|
// making it suitable for use as an OIDC public key.
|
||||||
func MarshalJWKPublicKey(key PublicKey, exp ...time.Time) []byte {
|
func MarshalJWKPublicKey(key PublicKey, exp ...time.Time) []byte {
|
||||||
// thumbprint keys are alphabetically sorted and only include the necessary public parts
|
// thumbprint keys are alphabetically sorted and only include the necessary public parts
|
||||||
switch k := key.Key().(type) {
|
switch k := key.Key().(type) {
|
||||||
|
@ -135,29 +140,35 @@ func MarshalJWKPublicKey(key PublicKey, exp ...time.Time) []byte {
|
||||||
case *ecdsa.PublicKey:
|
case *ecdsa.PublicKey:
|
||||||
return MarshalECPublicKey(k, exp...)
|
return MarshalECPublicKey(k, exp...)
|
||||||
case *dsa.PublicKey:
|
case *dsa.PublicKey:
|
||||||
panic(EInvalidPublicKey)
|
panic(ErrInvalidPublicKey)
|
||||||
default:
|
default:
|
||||||
// this is unreachable because we know the types that we pass in
|
// this is unreachable because we know the types that we pass in
|
||||||
log.Printf("keytype: %t, %+v\n", key, key)
|
log.Printf("keytype: %t, %+v\n", key, key)
|
||||||
panic(EInvalidPublicKey)
|
panic(ErrInvalidPublicKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ThumbprintPublicKey(pub *PublicKey) string {
|
// ThumbprintPublicKey returns the SHA256 RFC-spec JWK thumbprint
|
||||||
return ThumbprintUntypedPublicKey(pub)
|
func ThumbprintPublicKey(pub PublicKey) string {
|
||||||
|
return ThumbprintUntypedPublicKey(pub.Key())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ThumbprintUntypedPublicKey is a non-typesafe version of ThumbprintPublicKey
|
||||||
|
// (but will still panic, to help you discover bugs in development rather than production).
|
||||||
func ThumbprintUntypedPublicKey(pub crypto.PublicKey) string {
|
func ThumbprintUntypedPublicKey(pub crypto.PublicKey) string {
|
||||||
switch p := pub.(type) {
|
switch p := pub.(type) {
|
||||||
|
case PublicKey:
|
||||||
|
return ThumbprintUntypedPublicKey(p.Key())
|
||||||
case *ecdsa.PublicKey:
|
case *ecdsa.PublicKey:
|
||||||
return ThumbprintECPublicKey(p)
|
return ThumbprintECPublicKey(p)
|
||||||
case *rsa.PublicKey:
|
case *rsa.PublicKey:
|
||||||
return ThumbprintRSAPublicKey(p)
|
return ThumbprintRSAPublicKey(p)
|
||||||
default:
|
default:
|
||||||
panic(EInvalidPublicKey)
|
panic(ErrInvalidPublicKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalECPublicKey will take an EC key and output a JWK, with optional expiration date
|
||||||
func MarshalECPublicKey(k *ecdsa.PublicKey, exp ...time.Time) []byte {
|
func MarshalECPublicKey(k *ecdsa.PublicKey, exp ...time.Time) []byte {
|
||||||
thumb := ThumbprintECPublicKey(k)
|
thumb := ThumbprintECPublicKey(k)
|
||||||
crv := k.Curve.Params().Name
|
crv := k.Curve.Params().Name
|
||||||
|
@ -170,6 +181,7 @@ func MarshalECPublicKey(k *ecdsa.PublicKey, exp ...time.Time) []byte {
|
||||||
return []byte(fmt.Sprintf(`{"kid":%q,"use":"sig",%s"crv":%q,"kty":"EC","x":%q,"y":%q}`, thumb, expstr, crv, x, y))
|
return []byte(fmt.Sprintf(`{"kid":%q,"use":"sig",%s"crv":%q,"kty":"EC","x":%q,"y":%q}`, thumb, expstr, crv, x, y))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalECPublicKeyWithoutKeyID will output the most minimal version of an EC JWK (no key id, no "use" flag, nada)
|
||||||
func MarshalECPublicKeyWithoutKeyID(k *ecdsa.PublicKey) []byte {
|
func MarshalECPublicKeyWithoutKeyID(k *ecdsa.PublicKey) []byte {
|
||||||
crv := k.Curve.Params().Name
|
crv := k.Curve.Params().Name
|
||||||
x := base64.RawURLEncoding.EncodeToString(k.X.Bytes())
|
x := base64.RawURLEncoding.EncodeToString(k.X.Bytes())
|
||||||
|
@ -177,12 +189,14 @@ func MarshalECPublicKeyWithoutKeyID(k *ecdsa.PublicKey) []byte {
|
||||||
return []byte(fmt.Sprintf(`{"crv":%q,"kty":"EC","x":%q,"y":%q}`, crv, x, y))
|
return []byte(fmt.Sprintf(`{"crv":%q,"kty":"EC","x":%q,"y":%q}`, crv, x, y))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ThumbprintECPublicKey will output a RFC-spec SHA256 JWK thumbprint of an EC public key
|
||||||
func ThumbprintECPublicKey(k *ecdsa.PublicKey) string {
|
func ThumbprintECPublicKey(k *ecdsa.PublicKey) string {
|
||||||
thumbprintable := MarshalECPublicKeyWithoutKeyID(k)
|
thumbprintable := MarshalECPublicKeyWithoutKeyID(k)
|
||||||
sha := sha256.Sum256(thumbprintable)
|
sha := sha256.Sum256(thumbprintable)
|
||||||
return base64.RawURLEncoding.EncodeToString(sha[:])
|
return base64.RawURLEncoding.EncodeToString(sha[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalRSAPublicKey will take an RSA key and output a JWK, with optional expiration date
|
||||||
func MarshalRSAPublicKey(p *rsa.PublicKey, exp ...time.Time) []byte {
|
func MarshalRSAPublicKey(p *rsa.PublicKey, exp ...time.Time) []byte {
|
||||||
thumb := ThumbprintRSAPublicKey(p)
|
thumb := ThumbprintRSAPublicKey(p)
|
||||||
e := base64.RawURLEncoding.EncodeToString(big.NewInt(int64(p.E)).Bytes())
|
e := base64.RawURLEncoding.EncodeToString(big.NewInt(int64(p.E)).Bytes())
|
||||||
|
@ -194,36 +208,26 @@ func MarshalRSAPublicKey(p *rsa.PublicKey, exp ...time.Time) []byte {
|
||||||
return []byte(fmt.Sprintf(`{"kid":%q,"use":"sig",%s"e":%q,"kty":"RSA","n":%q}`, thumb, expstr, e, n))
|
return []byte(fmt.Sprintf(`{"kid":%q,"use":"sig",%s"e":%q,"kty":"RSA","n":%q}`, thumb, expstr, e, n))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalRSAPublicKeyWithoutKeyID will output the most minimal version of an RSA JWK (no key id, no "use" flag, nada)
|
||||||
func MarshalRSAPublicKeyWithoutKeyID(p *rsa.PublicKey) []byte {
|
func MarshalRSAPublicKeyWithoutKeyID(p *rsa.PublicKey) []byte {
|
||||||
e := base64.RawURLEncoding.EncodeToString(big.NewInt(int64(p.E)).Bytes())
|
e := base64.RawURLEncoding.EncodeToString(big.NewInt(int64(p.E)).Bytes())
|
||||||
n := base64.RawURLEncoding.EncodeToString(p.N.Bytes())
|
n := base64.RawURLEncoding.EncodeToString(p.N.Bytes())
|
||||||
return []byte(fmt.Sprintf(`{"e":%q,"kty":"RSA","n":%q}`, e, n))
|
return []byte(fmt.Sprintf(`{"e":%q,"kty":"RSA","n":%q}`, e, n))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ThumbprintRSAPublicKey will output a RFC-spec SHA256 JWK thumbprint of an EC public key
|
||||||
func ThumbprintRSAPublicKey(p *rsa.PublicKey) string {
|
func ThumbprintRSAPublicKey(p *rsa.PublicKey) string {
|
||||||
thumbprintable := MarshalRSAPublicKeyWithoutKeyID(p)
|
thumbprintable := MarshalRSAPublicKeyWithoutKeyID(p)
|
||||||
sha := sha256.Sum256([]byte(thumbprintable))
|
sha := sha256.Sum256([]byte(thumbprintable))
|
||||||
return base64.RawURLEncoding.EncodeToString(sha[:])
|
return base64.RawURLEncoding.EncodeToString(sha[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParsePrivateKey will try to parse the bytes you give it
|
||||||
|
// in any of the supported formats: PEM, DER, PKCS8, PKCS1, SEC1, and JWK
|
||||||
func ParsePrivateKey(block []byte) (PrivateKey, error) {
|
func ParsePrivateKey(block []byte) (PrivateKey, error) {
|
||||||
var pemblock *pem.Block
|
blocks, err := getPEMBytes(block)
|
||||||
var blocks = make([][]byte, 0, 1)
|
if nil != err {
|
||||||
|
return nil, ErrParsePrivateKey
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse PEM blocks (openssl generates junk metadata blocks for ECs)
|
// Parse PEM blocks (openssl generates junk metadata blocks for ECs)
|
||||||
|
@ -236,9 +240,10 @@ func ParsePrivateKey(block []byte) (PrivateKey, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we didn't parse a key arleady, we failed
|
// If we didn't parse a key arleady, we failed
|
||||||
return nil, EParsePrivateKey
|
return nil, ErrParsePrivateKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParsePrivateKeyString calls ParsePrivateKey([]byte(key)) for all you lazy folk.
|
||||||
func ParsePrivateKeyString(block string) (PrivateKey, error) {
|
func ParsePrivateKeyString(block string) (PrivateKey, error) {
|
||||||
return ParsePrivateKey([]byte(block))
|
return ParsePrivateKey([]byte(block))
|
||||||
}
|
}
|
||||||
|
@ -255,7 +260,7 @@ func parsePrivateKey(der []byte) (PrivateKey, error) {
|
||||||
case *ecdsa.PrivateKey:
|
case *ecdsa.PrivateKey:
|
||||||
key = k
|
key = k
|
||||||
default:
|
default:
|
||||||
// ignore nil and unknown key types
|
err = errors.New("Only RSA and ECDSA (EC) Private Keys are supported")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,6 +287,108 @@ func parsePrivateKey(der []byte) (PrivateKey, error) {
|
||||||
return key, nil
|
return key, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("no PEM blocks found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParsePrivateKey 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 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
|
||||||
|
for i, _ := range blocks {
|
||||||
|
block = blocks[i]
|
||||||
|
if key, err := parsePublicKey(block); nil == err {
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
var key PublicKey
|
||||||
|
|
||||||
|
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:
|
||||||
|
err = errors.New("Only RSA and ECDSA (EC) Public Keys are supported")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//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:
|
||||||
|
err = errors.New("Only RSA and ECDSA (EC) Public Keys are supported")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if nil != err {
|
||||||
|
//fmt.Println("3. ParsePKCS1PrublicKey")
|
||||||
|
keyx, err := x509.ParsePKCS1PublicKey(der)
|
||||||
|
key = NewPublicKey(keyx)
|
||||||
|
if nil != err {
|
||||||
|
//fmt.Println("4. ParseJWKPublicKey")
|
||||||
|
key, err = ParseJWKPublicKey(der)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewJWKPublicKey contstructs a PublicKey from the relevant pieces a map[string]string (generic JSON)
|
||||||
func NewJWKPublicKey(m map[string]string) (PublicKey, error) {
|
func NewJWKPublicKey(m map[string]string) (PublicKey, error) {
|
||||||
switch m["kty"] {
|
switch m["kty"] {
|
||||||
case "RSA":
|
case "RSA":
|
||||||
|
@ -289,22 +396,26 @@ func NewJWKPublicKey(m map[string]string) (PublicKey, error) {
|
||||||
case "EC":
|
case "EC":
|
||||||
return parseECPublicKey(m)
|
return parseECPublicKey(m)
|
||||||
default:
|
default:
|
||||||
return nil, EInvalidKeyType
|
return nil, ErrInvalidKeyType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseJWKPublicKey parses a JSON-encoded JWK and returns a PublicKey, or a (hopefully) helpful error message
|
||||||
func ParseJWKPublicKey(b []byte) (PublicKey, error) {
|
func ParseJWKPublicKey(b []byte) (PublicKey, error) {
|
||||||
return newJWKPublicKey(b)
|
return newJWKPublicKey(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseJWKPublicKeyString calls ParseJWKPublicKey([]byte(key)) for all you lazy folk.
|
||||||
func ParseJWKPublicKeyString(s string) (PublicKey, error) {
|
func ParseJWKPublicKeyString(s string) (PublicKey, error) {
|
||||||
return newJWKPublicKey(s)
|
return newJWKPublicKey(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DecodeJWKPublicKey stream-decodes a JSON-encoded JWK and returns a PublicKey, or a (hopefully) helpful error message
|
||||||
func DecodeJWKPublicKey(r io.Reader) (PublicKey, error) {
|
func DecodeJWKPublicKey(r io.Reader) (PublicKey, error) {
|
||||||
return newJWKPublicKey(r)
|
return newJWKPublicKey(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the underpinnings of the parser as used by the typesafe wrappers
|
||||||
func newJWKPublicKey(data interface{}) (PublicKey, error) {
|
func newJWKPublicKey(data interface{}) (PublicKey, error) {
|
||||||
var m map[string]string
|
var m map[string]string
|
||||||
|
|
||||||
|
@ -331,6 +442,7 @@ func newJWKPublicKey(data interface{}) (PublicKey, error) {
|
||||||
return NewJWKPublicKey(m)
|
return NewJWKPublicKey(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseJWKPrivateKey parses a JSON-encoded JWK and returns a PrivateKey, or a (hopefully) helpful error message
|
||||||
func ParseJWKPrivateKey(b []byte) (PrivateKey, error) {
|
func ParseJWKPrivateKey(b []byte) (PrivateKey, error) {
|
||||||
var m map[string]string
|
var m map[string]string
|
||||||
if err := json.Unmarshal(b, &m); nil != err {
|
if err := json.Unmarshal(b, &m); nil != err {
|
||||||
|
@ -343,7 +455,7 @@ func ParseJWKPrivateKey(b []byte) (PrivateKey, error) {
|
||||||
case "EC":
|
case "EC":
|
||||||
return parseECPrivateKey(m)
|
return parseECPrivateKey(m)
|
||||||
default:
|
default:
|
||||||
return nil, EInvalidKeyType
|
return nil, ErrInvalidKeyType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,7 +465,7 @@ func parseRSAPublicKey(m map[string]string) (*RSAPublicKey, error) {
|
||||||
n, _ := base64.RawURLEncoding.DecodeString(m["n"])
|
n, _ := base64.RawURLEncoding.DecodeString(m["n"])
|
||||||
e, _ := base64.RawURLEncoding.DecodeString(m["e"])
|
e, _ := base64.RawURLEncoding.DecodeString(m["e"])
|
||||||
if 0 == len(n) || 0 == len(e) {
|
if 0 == len(n) || 0 == len(e) {
|
||||||
return nil, EParseJWK
|
return nil, ErrParseJWK
|
||||||
}
|
}
|
||||||
ni := &big.Int{}
|
ni := &big.Int{}
|
||||||
ni.SetBytes(n)
|
ni.SetBytes(n)
|
||||||
|
@ -384,7 +496,7 @@ func parseRSAPrivateKey(m map[string]string) (key *rsa.PrivateKey, err error) {
|
||||||
dq, _ := base64.RawURLEncoding.DecodeString(m["dq"])
|
dq, _ := base64.RawURLEncoding.DecodeString(m["dq"])
|
||||||
qinv, _ := base64.RawURLEncoding.DecodeString(m["qi"])
|
qinv, _ := base64.RawURLEncoding.DecodeString(m["qi"])
|
||||||
if 0 == len(d) || 0 == len(p) || 0 == len(dp) || 0 == len(dq) || 0 == len(qinv) {
|
if 0 == len(d) || 0 == len(p) || 0 == len(dp) || 0 == len(dq) || 0 == len(qinv) {
|
||||||
return nil, EParseJWK
|
return nil, ErrParseJWK
|
||||||
}
|
}
|
||||||
|
|
||||||
di := &big.Int{}
|
di := &big.Int{}
|
||||||
|
@ -420,7 +532,7 @@ func parseECPublicKey(m map[string]string) (*ECPublicKey, error) {
|
||||||
x, _ := base64.RawURLEncoding.DecodeString(m["x"])
|
x, _ := base64.RawURLEncoding.DecodeString(m["x"])
|
||||||
y, _ := base64.RawURLEncoding.DecodeString(m["y"])
|
y, _ := base64.RawURLEncoding.DecodeString(m["y"])
|
||||||
if 0 == len(x) || 0 == len(y) || 0 == len(m["crv"]) {
|
if 0 == len(x) || 0 == len(y) || 0 == len(m["crv"]) {
|
||||||
return nil, EParseJWK
|
return nil, ErrParseJWK
|
||||||
}
|
}
|
||||||
|
|
||||||
xi := &big.Int{}
|
xi := &big.Int{}
|
||||||
|
@ -438,7 +550,7 @@ func parseECPublicKey(m map[string]string) (*ECPublicKey, error) {
|
||||||
case "P-521":
|
case "P-521":
|
||||||
crv = elliptic.P521()
|
crv = elliptic.P521()
|
||||||
default:
|
default:
|
||||||
return nil, EInvalidCurve
|
return nil, ErrInvalidCurve
|
||||||
}
|
}
|
||||||
|
|
||||||
pub := &ecdsa.PublicKey{
|
pub := &ecdsa.PublicKey{
|
||||||
|
@ -461,7 +573,7 @@ func parseECPrivateKey(m map[string]string) (*ecdsa.PrivateKey, error) {
|
||||||
|
|
||||||
d, _ := base64.RawURLEncoding.DecodeString(m["d"])
|
d, _ := base64.RawURLEncoding.DecodeString(m["d"])
|
||||||
if 0 == len(d) {
|
if 0 == len(d) {
|
||||||
return nil, EParseJWK
|
return nil, ErrParseJWK
|
||||||
}
|
}
|
||||||
di := &big.Int{}
|
di := &big.Int{}
|
||||||
di.SetBytes(d)
|
di.SetBytes(d)
|
||||||
|
|
|
@ -4,6 +4,8 @@ import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -67,3 +69,19 @@ func TestParsePrivateKeyRSA(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseCertificate(t *testing.T) {
|
||||||
|
resp, err := http.Get("http://bigsquid.auth0.com/pem")
|
||||||
|
if nil != err {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
bytes, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if nil != err {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = ParsePublicKey(bytes)
|
||||||
|
if nil != err {
|
||||||
|
log.Fatal("Could not parse PEM/cert from auth0")
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
@ -16,9 +17,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestServeKeys(t *testing.T) {
|
func TestServeKeys(t *testing.T) {
|
||||||
key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
eckey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
pubs := []keypairs.PublicKey{
|
pubs := []keypairs.PublicKey{
|
||||||
keypairs.NewPublicKey(key.Public()),
|
keypairs.NewPublicKey(eckey.Public()),
|
||||||
}
|
}
|
||||||
|
|
||||||
addr := ":62017"
|
addr := ":62017"
|
||||||
|
@ -48,12 +49,12 @@ func TestServeKeys(t *testing.T) {
|
||||||
m := map[string]string{}
|
m := map[string]string{}
|
||||||
resp, err := http.Get("http://localhost" + addr + "/.well-known/openid-configuration")
|
resp, err := http.Get("http://localhost" + addr + "/.well-known/openid-configuration")
|
||||||
if nil != err {
|
if nil != err {
|
||||||
panic(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
dec := json.NewDecoder(resp.Body)
|
dec := json.NewDecoder(resp.Body)
|
||||||
err = dec.Decode(&m)
|
err = dec.Decode(&m)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
panic(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
n := struct {
|
n := struct {
|
||||||
|
@ -63,15 +64,29 @@ func TestServeKeys(t *testing.T) {
|
||||||
}
|
}
|
||||||
resp, err = http.Get(m["jwks_uri"])
|
resp, err = http.Get(m["jwks_uri"])
|
||||||
if nil != err {
|
if nil != err {
|
||||||
panic(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
dec = json.NewDecoder(resp.Body)
|
dec = json.NewDecoder(resp.Body)
|
||||||
err = dec.Decode(&n)
|
err = dec.Decode(&n)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
panic(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
h.Shutdown(context.Background())
|
|
||||||
|
|
||||||
|
resp, err = http.Get("http://localhost" + addr + "/pem")
|
||||||
|
if nil != err {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
bytes, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if nil != err {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = keypairs.ParsePublicKey(bytes)
|
||||||
|
if nil != err {
|
||||||
|
log.Fatal("Could not parse PEM/cert from self")
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
h.Shutdown(context.Background())
|
||||||
<-done
|
<-done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue