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
}