test ec private keys

This commit is contained in:
AJ ONeal 2019-02-07 19:30:04 +00:00
parent 5695a0d0e7
commit b9f5fd197e
8 changed files with 160 additions and 64 deletions

View File

@ -0,0 +1,7 @@
{
"kty": "EC",
"crv": "P-256",
"d": "iYydo27aNGO9DBUWeGEPD8oNi1LZDqfxPmQlieLBjVQ",
"x": "IT1SWLxsacPiE5Z16jkopAn8_-85rMjgyCokrnjDft4",
"y": "mP2JwOAOdMmXuwpxbKng3KZz27mz-nKWIlXJ3rzSGMo"
}

View File

@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgiYydo27aNGO9DBUW
eGEPD8oNi1LZDqfxPmQlieLBjVShRANCAAQhPVJYvGxpw+ITlnXqOSikCfz/7zms
yODIKiSueMN+3pj9icDgDnTJl7sKcWyp4Nymc9u5s/pyliJVyd680hjK
-----END PRIVATE KEY-----

View File

@ -0,0 +1,8 @@
-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIImMnaNu2jRjvQwVFnhhDw/KDYtS2Q6n8T5kJYniwY1UoAoGCCqGSM49
AwEHoUQDQgAEIT1SWLxsacPiE5Z16jkopAn8/+85rMjgyCokrnjDft6Y/YnA4A50
yZe7CnFsqeDcpnPbubP6cpYiVcnevNIYyg==
-----END EC PRIVATE KEY-----

View File

@ -0,0 +1,7 @@
{
"kty": "EC",
"crv": "P-384",
"d": "XlyuCEWSTTS8U79O_Mz05z18vh4kb10szvu_7pdXuGWV6lfEyPExyUYWsA6A2kdV",
"x": "2zEU0bKCa7ejKLIJ8oPGnLhqhxyiv4_w38K2a0SPC6dsSd9_glNJ8lcqv0sff5Gb",
"y": "VD4jnu83S6scn6_TeAj3EZOREGbOs6dzoVpaugn-XQMMyC9O4VLbDDFGBZTJlMsb"
}

View File

@ -0,0 +1,6 @@
-----BEGIN PRIVATE KEY-----
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBeXK4IRZJNNLxTv078
zPTnPXy+HiRvXSzO+7/ul1e4ZZXqV8TI8THJRhawDoDaR1WhZANiAATbMRTRsoJr
t6Mosgnyg8acuGqHHKK/j/DfwrZrRI8Lp2xJ33+CU0nyVyq/Sx9/kZtUPiOe7zdL
qxyfr9N4CPcRk5EQZs6zp3OhWlq6Cf5dAwzIL07hUtsMMUYFlMmUyxs=
-----END PRIVATE KEY-----

View File

@ -0,0 +1,6 @@
-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDBeXK4IRZJNNLxTv078zPTnPXy+HiRvXSzO+7/ul1e4ZZXqV8TI8THJ
RhawDoDaR1WgBwYFK4EEACKhZANiAATbMRTRsoJrt6Mosgnyg8acuGqHHKK/j/Df
wrZrRI8Lp2xJ33+CU0nyVyq/Sx9/kZtUPiOe7zdLqxyfr9N4CPcRk5EQZs6zp3Oh
Wlq6Cf5dAwzIL07hUtsMMUYFlMmUyxs=
-----END EC PRIVATE KEY-----

View File

@ -52,6 +52,8 @@ type PublicJWK struct {
//BareJWK string `json:"-"` //BareJWK string `json:"-"`
thumbprint thumbstr `json:"thumbprint"` thumbprint thumbstr `json:"thumbprint"`
jwk jwkstr `json:"jwk"` jwk jwkstr `json:"jwk"`
x string
y string
} }
func (p *PublicJWK) Thumbprint() string { func (p *PublicJWK) Thumbprint() string {
@ -68,9 +70,9 @@ func PackPublicJWK(key crypto.PublicKey) (pub PublicJWK) {
// 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.(type) { switch k := key.(type) {
case *rsa.PublicKey: case *rsa.PublicKey:
pub = packPublicRSAJWK(k) pub = marshalRSAPublicKey(k)
case *ecdsa.PublicKey: case *ecdsa.PublicKey:
pub = packPublicECJWK(k) pub = MarshalECPublicKey(k)
case *dsa.PublicKey: case *dsa.PublicKey:
panic(EInvalidPublicKey) panic(EInvalidPublicKey)
default: default:
@ -81,17 +83,24 @@ func PackPublicJWK(key crypto.PublicKey) (pub PublicJWK) {
return return
} }
func packPublicECJWK(k *ecdsa.PublicKey) (pub PublicJWK) { func MarshalECPublicKey(k *ecdsa.PublicKey) PublicJWK {
pub := PublicJWK{}
pub.thumbprint = thumbstr(ThumbprintECPublicKey(k))
x := base64.RawURLEncoding.EncodeToString(k.X.Bytes()) x := base64.RawURLEncoding.EncodeToString(k.X.Bytes())
y := base64.RawURLEncoding.EncodeToString(k.Y.Bytes()) y := base64.RawURLEncoding.EncodeToString(k.Y.Bytes())
thumbprintable := fmt.Sprintf(`{"crv":%q,"kty":"EC","x":%q,"y":%q}`, k.Curve, x, y)
sha := sha256.Sum256([]byte(thumbprintable))
pub.thumbprint = thumbstr(base64.RawURLEncoding.EncodeToString(sha[:]))
pub.jwk = jwkstr(fmt.Sprintf(`{"kid":%q,"crv":%q,"kty":"EC","x":%q,"y":%q}`, pub.Thumbprint(), k.Curve, x, y)) pub.jwk = jwkstr(fmt.Sprintf(`{"kid":%q,"crv":%q,"kty":"EC","x":%q,"y":%q}`, pub.Thumbprint(), k.Curve, x, y))
return return pub
} }
func packPublicRSAJWK(k *rsa.PublicKey) (pub PublicJWK) { func ThumbprintECPublicKey(k *ecdsa.PublicKey) string {
x := base64.RawURLEncoding.EncodeToString(k.X.Bytes())
y := base64.RawURLEncoding.EncodeToString(k.Y.Bytes())
thumbprintable := []byte(fmt.Sprintf(`{"crv":%q,"kty":"EC","x":%q,"y":%q}`, k.Curve, x, y))
sha := sha256.Sum256(thumbprintable)
return base64.RawURLEncoding.EncodeToString(sha[:])
}
func marshalRSAPublicKey(k *rsa.PublicKey) (pub PublicJWK) {
e := base64.RawURLEncoding.EncodeToString(big.NewInt(int64(k.E)).Bytes()) e := base64.RawURLEncoding.EncodeToString(big.NewInt(int64(k.E)).Bytes())
n := base64.RawURLEncoding.EncodeToString(k.N.Bytes()) n := base64.RawURLEncoding.EncodeToString(k.N.Bytes())
thumbprintable := fmt.Sprintf(`{"e":%q,"kty":"RSA","n":%q}`, e, n) thumbprintable := fmt.Sprintf(`{"e":%q,"kty":"RSA","n":%q}`, e, n)
@ -101,9 +110,9 @@ func packPublicRSAJWK(k *rsa.PublicKey) (pub PublicJWK) {
return return
} }
func ParsePrivateKey(block []byte) (key PrivateKey, err error) { func ParsePrivateKey(block []byte) (PrivateKey, error) {
var pemblock *pem.Block var pemblock *pem.Block
var blocks [][]byte = make([][]byte, 1) var blocks = make([][]byte, 0, 1)
// Parse the PEM, if it's a pem // Parse the PEM, if it's a pem
for { for {
@ -112,7 +121,8 @@ func ParsePrivateKey(block []byte) (key PrivateKey, err error) {
// got one block, there may be more // got one block, there may be more
blocks = append(blocks, pemblock.Bytes) blocks = append(blocks, pemblock.Bytes)
} else { } else {
// the leftovers are not PEM blocks // the last block was not a PEM block
// therefore the next isn't either
if 0 != len(block) { if 0 != len(block) {
blocks = append(blocks, block) blocks = append(blocks, block)
} }
@ -120,83 +130,97 @@ func ParsePrivateKey(block []byte) (key PrivateKey, err error) {
} }
} }
//fmt.Println("Blocks:")
//fmt.Println(blocks)
// Parse PEM blocks (openssl generates junk metadata blocks for ECs) // Parse PEM blocks (openssl generates junk metadata blocks for ECs)
// or the original DER, or the JWK // or the original DER, or the JWK
for i, _ := range blocks { for i, _ := range blocks {
block = blocks[i] block = blocks[i]
if key = parsePrivateKey(block); nil != key { if key, err := parsePrivateKey(block); nil == err {
return return key, nil
} }
} }
// If we didn't parse a key arleady, we failed // If we didn't parse a key arleady, we failed
err = EParsePrivateKey return nil, EParsePrivateKey
return
} }
func parsePrivateKey(der []byte) (key PrivateKey) { func parsePrivateKey(der []byte) (PrivateKey, error) {
xkey, _ := x509.ParsePKCS8PrivateKey(der) var key PrivateKey
switch k := xkey.(type) {
case *rsa.PrivateKey:
key = k
case *ecdsa.PrivateKey:
key = k
default:
// ignore nil and unknown key types
}
key = xkey.(*rsa.PrivateKey) xkey, err := x509.ParsePKCS8PrivateKey(der)
if nil == key { if nil == err {
key, _ = x509.ParseECPrivateKey(der) switch k := xkey.(type) {
if nil == key { case *rsa.PrivateKey:
key, _ = x509.ParsePKCS1PrivateKey(der) key = k
if nil == key { case *ecdsa.PrivateKey:
key, _ = ParseJWKPrivateKey(der) key = k
default:
// ignore nil and unknown key types
}
}
fmt.Println("1. ParsePKCS8PrivateKey")
if nil != err {
key, err = x509.ParseECPrivateKey(der)
fmt.Println("2. ParseECPrivateKey")
if nil != err {
key, err = x509.ParsePKCS1PrivateKey(der)
fmt.Println("3. ParsePKCS1PrivateKey")
if nil != err {
key, err = ParseJWKPrivateKey(der)
fmt.Println("4. ParseJWKPrivateKey")
} }
} }
} }
return
// 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
} }
func ParseJWKPublicKey(b []byte) (key crypto.PublicKey, err error) { func ParseJWKPublicKey(b []byte) (crypto.PublicKey, error) {
var m map[string]string m := make(map[string]string)
err = json.Unmarshal(b, &m) err := json.Unmarshal(b, &m)
if nil != err { if nil != err {
return return nil, err
} }
switch m["kty"] { switch m["kty"] {
case "RSA": case "RSA":
key, err = parsePublicRSAJWK(m) return parseRSAPublicKey(m)
case "EC": case "EC":
key, err = parsePublicECJWK(m) return parseECPublicKey(m)
default: default:
err = EInvalidKeyType err = EInvalidKeyType
} }
return return nil, err
} }
func ParseJWKPrivateKey(b []byte) (key PrivateKey, err error) { func ParseJWKPrivateKey(b []byte) (PrivateKey, error) {
var m map[string]string var m map[string]string
err = json.Unmarshal(b, &m) if err := json.Unmarshal(b, &m); nil != err {
if nil != err { return nil, err
return
} }
switch m["kty"] { switch m["kty"] {
case "RSA": case "RSA":
key, err = parsePrivateRSAJWK(m) return parseRSAPrivateKey(m)
case "EC": case "EC":
key, err = parsePrivateECJWK(m) return parseECPrivateKey(m)
default: default:
err = EInvalidKeyType return nil, EInvalidKeyType
} }
return
} }
func parsePublicRSAJWK(m map[string]string) (pub *rsa.PublicKey, err error) { func parseRSAPublicKey(m map[string]string) (pub *rsa.PublicKey, err 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) {
@ -215,10 +239,10 @@ func parsePublicRSAJWK(m map[string]string) (pub *rsa.PublicKey, err error) {
return return
} }
func parsePrivateRSAJWK(m map[string]string) (key *rsa.PrivateKey, err error) { func parseRSAPrivateKey(m map[string]string) (key *rsa.PrivateKey, err error) {
var pub *rsa.PublicKey var pub *rsa.PublicKey
pub, err = parsePublicRSAJWK(m) pub, err = parseRSAPublicKey(m)
if nil != err { if nil != err {
return return
} }
@ -260,9 +284,9 @@ func parsePrivateRSAJWK(m map[string]string) (key *rsa.PrivateKey, err error) {
return return
} }
func parsePublicECJWK(m map[string]string) (pub *ecdsa.PublicKey, err error) { func parseECPublicKey(m map[string]string) (pub *ecdsa.PublicKey, err error) {
x, _ := base64.RawURLEncoding.DecodeString(m["n"]) x, _ := base64.RawURLEncoding.DecodeString(m["x"])
y, _ := base64.RawURLEncoding.DecodeString(m["e"]) 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, EParseJWK
} }
@ -295,12 +319,10 @@ func parsePublicECJWK(m map[string]string) (pub *ecdsa.PublicKey, err error) {
return return
} }
func parsePrivateECJWK(m map[string]string) (key *ecdsa.PrivateKey, err error) { func parseECPrivateKey(m map[string]string) (*ecdsa.PrivateKey, error) {
var pub *ecdsa.PublicKey pub, err := parseECPublicKey(m)
pub, err = parsePublicECJWK(m)
if nil != err { if nil != err {
return return nil, err
} }
d, _ := base64.RawURLEncoding.DecodeString(m["d"]) d, _ := base64.RawURLEncoding.DecodeString(m["d"])
@ -310,10 +332,8 @@ func parsePrivateECJWK(m map[string]string) (key *ecdsa.PrivateKey, err error) {
di := &big.Int{} di := &big.Int{}
di.SetBytes(d) di.SetBytes(d)
key = &ecdsa.PrivateKey{ return &ecdsa.PrivateKey{
PublicKey: *pub, PublicKey: *pub,
D: di, D: di,
} }, nil
return
} }

37
keypairs_test.go Normal file
View File

@ -0,0 +1,37 @@
package keypairs
import (
"crypto/ecdsa"
"fmt"
"io/ioutil"
"testing"
)
func TestLoadECJWK(t *testing.T) {
// TODO package all fixtures with fileb0x
keypaths := []string{
"fixtures/privkey-ec-p256.jwk.json",
"fixtures/privkey-ec-p256.sec1.pem", // has openssl EC Param block
"fixtures/privkey-ec-p256.pkcs8.pem",
"fixtures/privkey-ec-p384.jwk.json",
"fixtures/privkey-ec-p384.sec1.pem",
"fixtures/privkey-ec-p384.pkcs8.pem",
}
for i := range keypaths {
path := keypaths[i]
fmt.Println("\n", path)
b, err := ioutil.ReadFile(path)
if nil != err {
t.Fatal(err)
}
key, err := ParsePrivateKey(b)
if nil != err {
t.Fatal(err)
}
eckey := key.(*ecdsa.PrivateKey)
thumb := ThumbprintECPublicKey(eckey.Public().(*ecdsa.PublicKey))
fmt.Println(thumb)
}
}