diff --git a/fixtures/privkey-rsa-2048.jwk.json b/fixtures/privkey-rsa-2048.jwk.json new file mode 100644 index 0000000..f344c22 --- /dev/null +++ b/fixtures/privkey-rsa-2048.jwk.json @@ -0,0 +1,11 @@ +{ + "kty": "RSA", + "n": "m2ttVBxPlWw06ZmGBWVDlfjkPAJ4DgnY0TrDwtCohHzLxGhDNzUJefLukC-xu0LBKylYojT5vTkxaOhxeSYo31syu4WhxbkTBLICOFcCGMob6pSQ38P8LdAIlb0pqDHxEJ9adWomjuFf0SUhN1cP7s9m8Yk9trkpEqjskocn2BOnTB57qAZM6-I70on0_iDZm7-jcqOPgADAmbWHhy67BXkk4yy_YzD4yOGZFXZcNp915_TW5bRd__AKPHUHxJasPiyEFqlNKBR2DSD-LbX5eTmzCh2ikrwTMja7mUdBJf2bK3By5AB0Qi49OykUCfNZeQlEz7UNNj9RGps_50-CNw", + "e": "AQAB", + "d": "Cpfo7Mm9Nu8YMC_xrZ54W9mKHPkCG9rZ93Ds9PNp-RXUgb-ljTbFPZWsYxGNKLllFz8LNosr1pT2ZDMrwNk0Af1iWNvD6gkyXaiQdCyiDPSBsJyNv2LJZon-e85X74nv53UlIkmo9SYxdLz2JaJ-iIWEe8Qh-7llLktrTJV_xr98_tbhgSppz_IeOymq3SEZaQHM8pTU7w7XvCj2pb9r8fN0M0XcgWZIaf3LGEfkhF_WtX67XJ0C6-LbkT51jtlLRNGX6haGdscXS0OWWjKOJzKGuV-NbthEn5rmRtVnjRZ3yaxQ0ud8vC-NONn7yvGUlOur1IdDzJ_YfHPt9sHMQQ", + "p": "ynG-t9HwKCN3MWRYFdnFzi9-02Qcy3p8B5pu3ary2E70hYn2pHlUG2a9BNE8c5xHQ3Hx43WoWf6s0zOunPV1G28LkU_UYEbAtPv_PxSmzpQp9n9XnYvBLBF8Y3z7gxgLn1vVFNARrQdRtj87qY3aw7E9S4DsGcAarIuOT2TsTCE", + "q": "xIkAjgUzB1zaUzJtW2Zgvp9cYYr1DmpH30ePZl3c_8397_DZDDo46fnFYjs6uPa03HpmKUnbjwr14QHlfXlntJBEuXxcqLjkdKdJ4ob7xueLTK4suo9V8LSrkLChVxlZQwnFD2E5ll0sVeeDeMJHQw38ahSrBFEVnxjpnPh1Q1c", + "dp": "tzDGjECFOU0ehqtuqhcuT63a7h8hj19-7MJqoFwY9HQ-ALkfXyYLXeBSGxHbyiIYuodZg6LsfMNgUJ3r3Eyhc_nAVfYPEC_2IdAG4WYmq7iXYF9LQV09qEsKbFykm7QekE3hO7wswo5k-q2tp3ieBYdVGAXJoGOdv5VpaZ7B1QE", + "dq": "kh5dyDk7YCz7sUFbpsmuAeuPjoH2ghooh2u3xN7iUVmAg-ToKjwbVnG5-7eXiC779rQVwnrD_0yh1AFJ8wjRPqDIR7ObXGHikIxT1VSQWqiJm6AfZzDsL0LUD4YS3iPdhob7-NxLKWzqao_u4lhnDQaX9PKa12HFlny6K1daL48", + "qi": "AlHWbx1gp6Z9pbw_1hlS7HuXAgWoX7IjbTUelldf4gkriDWLOrj3QCZcO4ZvZvEwJhVlsny9LO8IkbwGJEL6cXraK08ByVS2mwQyflgTgGNnpzixyEUL_mrQLx6y145FHcxfeqNInMhep-0Mxn1D5nlhmIOgRApS0t9VoXtHhFU" +} diff --git a/fixtures/privkey-rsa-2048.pkcs1.pem b/fixtures/privkey-rsa-2048.pkcs1.pem new file mode 100644 index 0000000..246bd35 --- /dev/null +++ b/fixtures/privkey-rsa-2048.pkcs1.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAm2ttVBxPlWw06ZmGBWVDlfjkPAJ4DgnY0TrDwtCohHzLxGhD +NzUJefLukC+xu0LBKylYojT5vTkxaOhxeSYo31syu4WhxbkTBLICOFcCGMob6pSQ +38P8LdAIlb0pqDHxEJ9adWomjuFf0SUhN1cP7s9m8Yk9trkpEqjskocn2BOnTB57 +qAZM6+I70on0/iDZm7+jcqOPgADAmbWHhy67BXkk4yy/YzD4yOGZFXZcNp915/TW +5bRd//AKPHUHxJasPiyEFqlNKBR2DSD+LbX5eTmzCh2ikrwTMja7mUdBJf2bK3By +5AB0Qi49OykUCfNZeQlEz7UNNj9RGps/50+CNwIDAQABAoIBAAqX6OzJvTbvGDAv +8a2eeFvZihz5Ahva2fdw7PTzafkV1IG/pY02xT2VrGMRjSi5ZRc/CzaLK9aU9mQz +K8DZNAH9Yljbw+oJMl2okHQsogz0gbCcjb9iyWaJ/nvOV++J7+d1JSJJqPUmMXS8 +9iWifoiFhHvEIfu5ZS5La0yVf8a/fP7W4YEqac/yHjspqt0hGWkBzPKU1O8O17wo +9qW/a/HzdDNF3IFmSGn9yxhH5IRf1rV+u1ydAuvi25E+dY7ZS0TRl+oWhnbHF0tD +lloyjicyhrlfjW7YRJ+a5kbVZ40Wd8msUNLnfLwvjTjZ+8rxlJTrq9SHQ8yf2Hxz +7fbBzEECgYEAynG+t9HwKCN3MWRYFdnFzi9+02Qcy3p8B5pu3ary2E70hYn2pHlU +G2a9BNE8c5xHQ3Hx43WoWf6s0zOunPV1G28LkU/UYEbAtPv/PxSmzpQp9n9XnYvB +LBF8Y3z7gxgLn1vVFNARrQdRtj87qY3aw7E9S4DsGcAarIuOT2TsTCECgYEAxIkA +jgUzB1zaUzJtW2Zgvp9cYYr1DmpH30ePZl3c/8397/DZDDo46fnFYjs6uPa03Hpm +KUnbjwr14QHlfXlntJBEuXxcqLjkdKdJ4ob7xueLTK4suo9V8LSrkLChVxlZQwnF +D2E5ll0sVeeDeMJHQw38ahSrBFEVnxjpnPh1Q1cCgYEAtzDGjECFOU0ehqtuqhcu +T63a7h8hj19+7MJqoFwY9HQ+ALkfXyYLXeBSGxHbyiIYuodZg6LsfMNgUJ3r3Eyh +c/nAVfYPEC/2IdAG4WYmq7iXYF9LQV09qEsKbFykm7QekE3hO7wswo5k+q2tp3ie +BYdVGAXJoGOdv5VpaZ7B1QECgYEAkh5dyDk7YCz7sUFbpsmuAeuPjoH2ghooh2u3 +xN7iUVmAg+ToKjwbVnG5+7eXiC779rQVwnrD/0yh1AFJ8wjRPqDIR7ObXGHikIxT +1VSQWqiJm6AfZzDsL0LUD4YS3iPdhob7+NxLKWzqao/u4lhnDQaX9PKa12HFlny6 +K1daL48CgYACUdZvHWCnpn2lvD/WGVLse5cCBahfsiNtNR6WV1/iCSuINYs6uPdA +Jlw7hm9m8TAmFWWyfL0s7wiRvAYkQvpxetorTwHJVLabBDJ+WBOAY2enOLHIRQv+ +atAvHrLXjkUdzF96o0icyF6n7QzGfUPmeWGYg6BEClLS31Whe0eEVQ== +-----END RSA PRIVATE KEY----- diff --git a/fixtures/privkey-rsa-2048.pkcs8.pem b/fixtures/privkey-rsa-2048.pkcs8.pem new file mode 100644 index 0000000..53dbf83 --- /dev/null +++ b/fixtures/privkey-rsa-2048.pkcs8.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCba21UHE+VbDTp +mYYFZUOV+OQ8AngOCdjROsPC0KiEfMvEaEM3NQl58u6QL7G7QsErKViiNPm9OTFo +6HF5JijfWzK7haHFuRMEsgI4VwIYyhvqlJDfw/wt0AiVvSmoMfEQn1p1aiaO4V/R +JSE3Vw/uz2bxiT22uSkSqOyShyfYE6dMHnuoBkzr4jvSifT+INmbv6Nyo4+AAMCZ +tYeHLrsFeSTjLL9jMPjI4ZkVdlw2n3Xn9NbltF3/8Ao8dQfElqw+LIQWqU0oFHYN +IP4ttfl5ObMKHaKSvBMyNruZR0El/ZsrcHLkAHRCLj07KRQJ81l5CUTPtQ02P1Ea +mz/nT4I3AgMBAAECggEACpfo7Mm9Nu8YMC/xrZ54W9mKHPkCG9rZ93Ds9PNp+RXU +gb+ljTbFPZWsYxGNKLllFz8LNosr1pT2ZDMrwNk0Af1iWNvD6gkyXaiQdCyiDPSB +sJyNv2LJZon+e85X74nv53UlIkmo9SYxdLz2JaJ+iIWEe8Qh+7llLktrTJV/xr98 +/tbhgSppz/IeOymq3SEZaQHM8pTU7w7XvCj2pb9r8fN0M0XcgWZIaf3LGEfkhF/W +tX67XJ0C6+LbkT51jtlLRNGX6haGdscXS0OWWjKOJzKGuV+NbthEn5rmRtVnjRZ3 +yaxQ0ud8vC+NONn7yvGUlOur1IdDzJ/YfHPt9sHMQQKBgQDKcb630fAoI3cxZFgV +2cXOL37TZBzLenwHmm7dqvLYTvSFifakeVQbZr0E0TxznEdDcfHjdahZ/qzTM66c +9XUbbwuRT9RgRsC0+/8/FKbOlCn2f1edi8EsEXxjfPuDGAufW9UU0BGtB1G2Pzup +jdrDsT1LgOwZwBqsi45PZOxMIQKBgQDEiQCOBTMHXNpTMm1bZmC+n1xhivUOakff +R49mXdz/zf3v8NkMOjjp+cViOzq49rTcemYpSduPCvXhAeV9eWe0kES5fFyouOR0 +p0nihvvG54tMriy6j1XwtKuQsKFXGVlDCcUPYTmWXSxV54N4wkdDDfxqFKsEURWf +GOmc+HVDVwKBgQC3MMaMQIU5TR6Gq26qFy5PrdruHyGPX37swmqgXBj0dD4AuR9f +Jgtd4FIbEdvKIhi6h1mDoux8w2BQnevcTKFz+cBV9g8QL/Yh0AbhZiaruJdgX0tB +XT2oSwpsXKSbtB6QTeE7vCzCjmT6ra2neJ4Fh1UYBcmgY52/lWlpnsHVAQKBgQCS +Hl3IOTtgLPuxQVumya4B64+OgfaCGiiHa7fE3uJRWYCD5OgqPBtWcbn7t5eILvv2 +tBXCesP/TKHUAUnzCNE+oMhHs5tcYeKQjFPVVJBaqImboB9nMOwvQtQPhhLeI92G +hvv43EspbOpqj+7iWGcNBpf08prXYcWWfLorV1ovjwKBgAJR1m8dYKemfaW8P9YZ +Uux7lwIFqF+yI201HpZXX+IJK4g1izq490AmXDuGb2bxMCYVZbJ8vSzvCJG8BiRC ++nF62itPAclUtpsEMn5YE4BjZ6c4schFC/5q0C8esteORR3MX3qjSJzIXqftDMZ9 +Q+Z5YZiDoEQKUtLfVaF7R4RV +-----END PRIVATE KEY----- diff --git a/keypairs.go b/keypairs.go index 27f1c68..190d7e0 100644 --- a/keypairs.go +++ b/keypairs.go @@ -70,7 +70,7 @@ func PackPublicJWK(key crypto.PublicKey) (pub PublicJWK) { // thumbprint keys are alphabetically sorted and only include the necessary public parts switch k := key.(type) { case *rsa.PublicKey: - pub = marshalRSAPublicKey(k) + pub = MarshalRSAPublicKey(k) case *ecdsa.PublicKey: pub = MarshalECPublicKey(k) case *dsa.PublicKey: @@ -83,31 +83,51 @@ func PackPublicJWK(key crypto.PublicKey) (pub PublicJWK) { return } +func ThumbprintPublicKey(pub crypto.PublicKey) string { + switch p := pub.(type) { + case *ecdsa.PublicKey: + return ThumbprintECPublicKey(p) + case *rsa.PublicKey: + return ThumbprintRSAPublicKey(p) + default: + panic(EInvalidPublicKey) + } +} + func MarshalECPublicKey(k *ecdsa.PublicKey) PublicJWK { pub := PublicJWK{} pub.thumbprint = thumbstr(ThumbprintECPublicKey(k)) + crv := k.Curve.Params().Name x := base64.RawURLEncoding.EncodeToString(k.X.Bytes()) y := base64.RawURLEncoding.EncodeToString(k.Y.Bytes()) - 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(), crv, x, y)) return pub } func ThumbprintECPublicKey(k *ecdsa.PublicKey) string { + crv := k.Curve.Params().Name 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)) + thumbprintable := []byte(fmt.Sprintf(`{"crv":%q,"kty":"EC","x":%q,"y":%q}`, crv, 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()) - n := base64.RawURLEncoding.EncodeToString(k.N.Bytes()) +func MarshalRSAPublicKey(p *rsa.PublicKey) PublicJWK { + pub := PublicJWK{} + pub.thumbprint = thumbstr(ThumbprintRSAPublicKey(p)) + e := base64.RawURLEncoding.EncodeToString(big.NewInt(int64(p.E)).Bytes()) + n := base64.RawURLEncoding.EncodeToString(p.N.Bytes()) + pub.jwk = jwkstr(fmt.Sprintf(`{"kid":%q,"e":%q,"kty":"RSA","n":%q}`, pub.Thumbprint(), e, n)) + return pub +} + +func ThumbprintRSAPublicKey(p *rsa.PublicKey) string { + e := base64.RawURLEncoding.EncodeToString(big.NewInt(int64(p.E)).Bytes()) + n := base64.RawURLEncoding.EncodeToString(p.N.Bytes()) thumbprintable := fmt.Sprintf(`{"e":%q,"kty":"RSA","n":%q}`, e, n) sha := sha256.Sum256([]byte(thumbprintable)) - pub.thumbprint = thumbstr(base64.RawURLEncoding.EncodeToString(sha[:])) - pub.jwk = jwkstr(fmt.Sprintf(`{"kid":%q,"e":%q,"kty":"RSA","n":%q}`, pub.Thumbprint(), e, n)) - return + return base64.RawURLEncoding.EncodeToString(sha[:]) } func ParsePrivateKey(block []byte) (PrivateKey, error) { @@ -130,9 +150,6 @@ func ParsePrivateKey(block []byte) (PrivateKey, error) { } } - //fmt.Println("Blocks:") - //fmt.Println(blocks) - // Parse PEM blocks (openssl generates junk metadata blocks for ECs) // or the original DER, or the JWK for i, _ := range blocks { @@ -149,6 +166,7 @@ func ParsePrivateKey(block []byte) (PrivateKey, error) { func parsePrivateKey(der []byte) (PrivateKey, error) { var key PrivateKey + //fmt.Println("1. ParsePKCS8PrivateKey") xkey, err := x509.ParsePKCS8PrivateKey(der) if nil == err { switch k := xkey.(type) { @@ -160,17 +178,16 @@ func parsePrivateKey(der []byte) (PrivateKey, error) { // ignore nil and unknown key types } } - fmt.Println("1. ParsePKCS8PrivateKey") if nil != err { + //fmt.Println("2. ParseECPrivateKey") key, err = x509.ParseECPrivateKey(der) - fmt.Println("2. ParseECPrivateKey") if nil != err { + //fmt.Println("3. ParsePKCS1PrivateKey") key, err = x509.ParsePKCS1PrivateKey(der) - fmt.Println("3. ParsePKCS1PrivateKey") if nil != err { + //fmt.Println("4. ParseJWKPrivateKey") key, err = ParseJWKPrivateKey(der) - fmt.Println("4. ParseJWKPrivateKey") } } } diff --git a/keypairs_test.go b/keypairs_test.go index 012b6c8..9ebdae3 100644 --- a/keypairs_test.go +++ b/keypairs_test.go @@ -2,36 +2,68 @@ package keypairs import ( "crypto/ecdsa" - "fmt" + "crypto/rsa" "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", +// TODO package all fixtures with fileb0x + +func TestParsePrivateKeyEC(t *testing.T) { + keys := [][]string{ + []string{"fixtures/privkey-ec-p256.jwk.json", "bJiCcZHuAF9dDetKEdGjJU3pFvFLoB_QHe9_6cAuY8c"}, + // has openssl EC Param block + []string{"fixtures/privkey-ec-p256.sec1.pem", "bJiCcZHuAF9dDetKEdGjJU3pFvFLoB_QHe9_6cAuY8c"}, + []string{"fixtures/privkey-ec-p256.pkcs8.pem", "bJiCcZHuAF9dDetKEdGjJU3pFvFLoB_QHe9_6cAuY8c"}, + + []string{"fixtures/privkey-ec-p384.jwk.json", "-WoRXrk3FZ7tGi8oj5wJHDDfFMBCGlUbpwil1WhpxrU"}, + []string{"fixtures/privkey-ec-p384.sec1.pem", "-WoRXrk3FZ7tGi8oj5wJHDDfFMBCGlUbpwil1WhpxrU"}, + []string{"fixtures/privkey-ec-p384.pkcs8.pem", "-WoRXrk3FZ7tGi8oj5wJHDDfFMBCGlUbpwil1WhpxrU"}, } - for i := range keypaths { - path := keypaths[i] - fmt.Println("\n", path) + for i := range keys { + path := keys[i][0] + thumb := keys[i][1] b, err := ioutil.ReadFile(path) if nil != err { - t.Fatal(err) + t.Fatal(path, err) } key, err := ParsePrivateKey(b) if nil != err { - t.Fatal(err) + t.Fatal(path, err) } eckey := key.(*ecdsa.PrivateKey) - thumb := ThumbprintECPublicKey(eckey.Public().(*ecdsa.PublicKey)) - fmt.Println(thumb) + thumb2 := ThumbprintECPublicKey(eckey.Public().(*ecdsa.PublicKey)) + if thumb != thumb2 { + t.Fatalf("EC thumbprints do not match: %q, %q, %q", path, thumb, thumb2) + } + } +} + +func TestParsePrivateKeyRSA(t *testing.T) { + keypaths := []string{ + "fixtures/privkey-rsa-2048.jwk.json", + "fixtures/privkey-rsa-2048.pkcs1.pem", + "fixtures/privkey-rsa-2048.pkcs8.pem", + } + for i := range keypaths { + path := keypaths[i] + b, err := ioutil.ReadFile(path) + if nil != err { + t.Fatal(path, err) + } + + key, err := ParsePrivateKey(b) + if nil != err { + t.Fatal(path, err) + } + + rsakey := key.(*rsa.PrivateKey) + thumb := "UIyZzFXPL-mTLnxQeSAHgu7gV16tro3evksnFb8fFQQ" + thumb2 := ThumbprintRSAPublicKey(rsakey.Public().(*rsa.PublicKey)) + if thumb != thumb2 { + t.Fatalf("RSA thumbprints do not match: %q, %q, %q", path, thumb, thumb2) + } } }