2019-02-07 01:10:30 +00:00
package keypairs
import (
"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-02-08 23:53:29 +00:00
"time"
2019-02-07 01:10:30 +00:00
)
2019-02-09 00:43:50 +00:00
var EInvalidPrivateKey = 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" )
2019-02-07 01:31:48 +00:00
var EParsePrivateKey = errors . New ( "PrivateKey bytes could not be parsed as PEM or DER (PKCS8, SEC1, or PKCS1) or JWK" )
var EParseJWK = errors . New ( "JWK is missing required base64-encoded JSON fields" )
var EInvalidKeyType = errors . New ( "The JWK's 'kty' must be either 'RSA' or 'EC'" )
var EInvalidCurve = errors . New ( "The JWK's 'crv' must be either of the NIST standards 'P-256' or 'P-384'" )
2019-02-08 23:53:29 +00:00
const EDevSwapPrivatePublic = "[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."
// 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
}
type ECPublicKey struct {
PublicKey * ecdsa . PublicKey // empty interface
KID string
Expiry time . Time
}
type RSAPublicKey struct {
PublicKey * rsa . PublicKey // empty interface
KID string
Expiry time . Time
}
func ( p * ECPublicKey ) Thumbprint ( ) string {
return ThumbprintUntypedPublicKey ( p . PublicKey )
}
2019-02-20 19:26:37 +00:00
func ( p * ECPublicKey ) KeyID ( ) string {
return p . KID
}
2019-02-08 23:53:29 +00:00
func ( p * ECPublicKey ) Key ( ) crypto . PublicKey {
return p . PublicKey
}
2019-02-09 00:43:50 +00:00
func ( p * ECPublicKey ) ExpireAt ( t time . Time ) {
p . Expiry = t
}
2019-02-08 23:53:29 +00:00
func ( p * RSAPublicKey ) Thumbprint ( ) string {
return ThumbprintUntypedPublicKey ( p . PublicKey )
}
2019-02-20 19:26:37 +00:00
func ( p * RSAPublicKey ) KeyID ( ) string {
return p . KID
}
2019-02-08 23:53:29 +00:00
func ( p * RSAPublicKey ) Key ( ) crypto . PublicKey {
return p . PublicKey
}
2019-02-09 00:43:50 +00:00
func ( p * RSAPublicKey ) ExpireAt ( t time . Time ) {
p . Expiry = t
}
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 :
panic ( errors . New ( EDevSwapPrivatePublic ) )
case * rsa . PrivateKey :
panic ( errors . New ( EDevSwapPrivatePublic ) )
case * dsa . PublicKey :
panic ( EInvalidPublicKey )
case * dsa . PrivateKey :
panic ( EInvalidPublicKey )
default :
panic ( errors . New ( fmt . Sprintf ( EDevBadKeyType , pub ) ) )
}
return k
}
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 :
panic ( EInvalidPublicKey )
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-02-07 01:10:30 +00:00
panic ( EInvalidPublicKey )
}
}
2019-02-08 23:53:29 +00:00
func ThumbprintPublicKey ( pub * PublicKey ) string {
return ThumbprintUntypedPublicKey ( pub )
}
func ThumbprintUntypedPublicKey ( pub crypto . PublicKey ) string {
2019-02-07 20:05:26 +00:00
switch p := pub . ( type ) {
case * ecdsa . PublicKey :
return ThumbprintECPublicKey ( p )
case * rsa . PublicKey :
return ThumbprintRSAPublicKey ( p )
default :
panic ( EInvalidPublicKey )
}
}
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-12 16:57:01 +00:00
return [ ] byte ( fmt . Sprintf ( ` { "kid":%q,"use":"sig",%s"crv":%q,"kty":"EC","x":%q,"y":%q} ` , expstr , thumb , crv , x , y ) )
2019-02-07 19:30:04 +00:00
}
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 ) )
}
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-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-12 16:57:01 +00:00
return [ ] byte ( fmt . Sprintf ( ` { "kid":%q,"use":"sig",%s"e":%q,"kty":"RSA","n":%q} ` , expstr , thumb , e , n ) )
2019-02-07 20:05:26 +00:00
}
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 ) )
}
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-02-07 19:30:04 +00:00
func ParsePrivateKey ( block [ ] byte ) ( PrivateKey , error ) {
2019-02-07 01:10:30 +00:00
var pemblock * pem . Block
2019-02-07 19:30:04 +00:00
var blocks = make ( [ ] [ ] byte , 0 , 1 )
2019-02-07 01:10:30 +00:00
// 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 {
2019-02-07 19:30:04 +00:00
// the last block was not a PEM block
// therefore the next isn't either
2019-02-07 01:10:30 +00:00
if 0 != len ( block ) {
blocks = append ( blocks , block )
}
break
}
}
// Parse PEM blocks (openssl generates junk metadata blocks for ECs)
// or the original DER, or the JWK
for i , _ := range blocks {
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
}
}
// If we didn't parse a key arleady, we failed
2019-02-07 19:30:04 +00:00
return nil , EParsePrivateKey
2019-02-07 01:10:30 +00:00
}
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 :
// ignore nil and unknown key types
}
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-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-02-08 01:26:45 +00:00
return nil , EInvalidKeyType
}
}
2019-02-08 23:53:29 +00:00
func ParseJWKPublicKey ( b [ ] byte ) ( PublicKey , error ) {
2019-02-08 01:26:45 +00:00
return newJWKPublicKey ( b )
}
2019-02-08 23:53:29 +00:00
func ParseJWKPublicKeyString ( s string ) ( PublicKey , error ) {
2019-02-08 01:26:45 +00:00
return newJWKPublicKey ( s )
}
2019-02-08 23:53:29 +00:00
func DecodeJWKPublicKey ( r io . Reader ) ( PublicKey , error ) {
2019-02-08 01:26:45 +00:00
return newJWKPublicKey ( r )
}
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 io . Reader :
m = make ( map [ string ] string )
if err := json . NewDecoder ( d ) . Decode ( & m ) ; nil != err {
return nil , err
}
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-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-02-07 19:30:04 +00:00
return nil , EInvalidKeyType
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-02-08 23:53:29 +00:00
return nil , EParseJWK
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 ) {
return nil , EParseJWK
}
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" ] ) {
return nil , EParseJWK
}
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-02-08 23:53:29 +00:00
return nil , EInvalidCurve
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 ) {
return nil , EParseJWK
}
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
}