Merge pull request #76 from square/cs/ec-support

Support elliptic curve keys
This commit is contained in:
Cedric Staub
2016-06-21 23:27:18 -07:00
committed by GitHub
9 changed files with 98 additions and 34 deletions
+37 -6
View File
@@ -23,7 +23,7 @@ package jceks
import (
"bytes"
"crypto/rsa"
"crypto"
"crypto/sha1"
"crypto/subtle"
"crypto/x509"
@@ -46,6 +46,7 @@ const (
var (
oidKeyProtector = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 42, 2, 17, 1, 1}
oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
oidPublicKeyEC = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
)
type encryptedPrivateKeyInfo struct {
@@ -65,11 +66,19 @@ type privateKeyEntry struct {
certs []*x509.Certificate
}
// From https://golang.org/src/crypto/x509/sec1.go (also: see RFC 5915)
type ecPrivateKey struct {
Version int
PrivateKey []byte
NamedCurveOID asn1.ObjectIdentifier `asn1:"optional,explicit,tag:0"`
PublicKey asn1.BitString `asn1:"optional,explicit,tag:1"`
}
func (e *privateKeyEntry) String() string {
return fmt.Sprintf("private-key: %s", e.date)
}
func (e *privateKeyEntry) Recover(password []byte) (*rsa.PrivateKey, error) {
func (e *privateKeyEntry) Recover(password []byte) (crypto.PrivateKey, error) {
var eKey encryptedPrivateKeyInfo
_, err := asn1.Unmarshal(e.encodedKey, &eKey)
if err != nil {
@@ -95,10 +104,32 @@ func (e *privateKeyEntry) Recover(password []byte) (*rsa.PrivateKey, error) {
if _, err := asn1.Unmarshal(decryptedKey, &pKey); err != nil {
return nil, err
}
if !pKey.Algo.Algorithm.Equal(oidPublicKeyRSA) {
return nil, fmt.Errorf("unsupported private-key algorithm: %v", pKey.Algo.Algorithm)
if pKey.Algo.Algorithm.Equal(oidPublicKeyRSA) {
return x509.ParsePKCS1PrivateKey(pKey.PrivateKey)
}
return x509.ParsePKCS1PrivateKey(pKey.PrivateKey)
if pKey.Algo.Algorithm.Equal(oidPublicKeyEC) {
// In a JCEKS file, the EC private key blob contains only the key itself, without the
// named curve OID. Instead, the named curve OID is in a separate field (in the algorithm
// indentifier for the keystore entry). However to parse the EC key properly we need the
// EC key blob to have the curve OID...
key := ecPrivateKey{}
oid := asn1.ObjectIdentifier{}
// Parse EC private key
_, err := asn1.Unmarshal(pKey.PrivateKey, &key)
if err != nil {
return nil, fmt.Errorf("problem parsing ec key asn.1 struct: %s", err)
}
// Parse named curve OID from algorithm identifier
_, err = asn1.Unmarshal(pKey.Algo.Parameters.FullBytes, &oid)
if err != nil {
return nil, fmt.Errorf("problem parsing ec key asn.1 struct: %s", err)
}
// Update key to add named curve info, re-marshal, and parse
key.NamedCurveOID = oid
raw, _ := asn1.Marshal(key)
return x509.ParseECPrivateKey(raw)
}
return nil, fmt.Errorf("unsupported private-key algorithm: %v", pKey.Algo.Algorithm)
}
type trustedCertEntry struct {
@@ -338,7 +369,7 @@ func (ks *KeyStore) Parse(r io.Reader, password []byte) error {
// nil if the private key does not exist or alias points to a non
// private key entry.
func (ks *KeyStore) GetPrivateKeyAndCerts(alias string, password []byte) (
key *rsa.PrivateKey, certs []*x509.Certificate, err error) {
key crypto.PrivateKey, certs []*x509.Certificate, err error) {
entry := ks.entries[alias]
if entry == nil {
+1 -1
View File
@@ -175,7 +175,7 @@ func TestPrivateKey(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if !equalRSAPrivateKey(key, expected) {
if !equalRSAPrivateKey(key.(*rsa.PrivateKey), expected) {
t.Fatalf("keys are not equal")
}
+32 -7
View File
@@ -18,6 +18,9 @@ package main
import (
"bufio"
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/binary"
@@ -28,6 +31,7 @@ import (
"os"
"path"
"path/filepath"
"reflect"
"strings"
"sync"
@@ -276,7 +280,6 @@ func readCertsFromFile(wg *sync.WaitGroup, reader io.Reader, filename string, fo
case "PEM":
scanner := pemScanner(reader)
for scanner.Scan() {
fmt.Fprintf(os.Stderr, "found a block\n")
block, _ := pem.Decode(scanner.Bytes())
if block.Type != "CERTIFICATE" {
// Skip non-certificate PEM blocks
@@ -290,7 +293,6 @@ func readCertsFromFile(wg *sync.WaitGroup, reader io.Reader, filename string, fo
wg.Add(1)
out <- certWithAlias{file: filename, cert: cert}
}
fmt.Fprintf(os.Stderr, "done\n")
case "DER":
data, err := ioutil.ReadAll(reader)
if err != nil {
@@ -422,11 +424,7 @@ func convertToPem(wg *sync.WaitGroup, reader io.Reader, filename string, format
os.Exit(1)
}
wg.Add(1)
out <- &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(key),
Headers: map[string]string{"alias": alias},
}
out <- keyToPem(key, map[string]string{"alias": alias})
for _, cert := range certs {
wg.Add(1)
out <- &pem.Block{
@@ -441,3 +439,30 @@ func convertToPem(wg *sync.WaitGroup, reader io.Reader, filename string, format
os.Exit(1)
}
}
// Convert a key into one or more PEM blocks for output
func keyToPem(key crypto.PrivateKey, headers map[string]string) *pem.Block {
switch k := key.(type) {
case *rsa.PrivateKey:
return &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(k),
Headers: headers,
}
case *ecdsa.PrivateKey:
raw, err := x509.MarshalECPrivateKey(k)
if err != nil {
fmt.Fprintf(os.Stderr, "error marshaling key: %s\n", reflect.TypeOf(key))
os.Exit(1)
}
return &pem.Block{
Type: "EC PRIVATE KEY",
Bytes: raw,
Headers: headers,
}
default:
fmt.Fprintf(os.Stderr, "unknown key type: %s\n", reflect.TypeOf(key))
os.Exit(1)
}
return nil
}
+1 -1
View File
@@ -64,7 +64,7 @@ example-expired.crt: example-expired.csr
# Example ECC key
example-elliptic-sha1.key:
openssl ecparam -genkey -name prime256v1 -out $@
openssl ecparam -genkey -name secp521r1 -out $@
example-elliptic-sha1.crt: example-elliptic-sha1.csr
openssl x509 -req -in $< -signkey $(@:.crt=.key) -out $@ -days 2500 -extfile openssl.ext -extensions leaf
+12 -9
View File
@@ -1,13 +1,16 @@
-----BEGIN CERTIFICATE-----
MIIB/zCCAaegAwIBAgIJAJo7hMg5Z4KeMAkGByqGSM49BAEwXjELMAkGA1UEBhMC
MIICiDCCAeqgAwIBAgIJAJ/xjMI6FKOAMAkGByqGSM49BAEwXjELMAkGA1UEBhMC
VVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQKEwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFt
cGxlMR4wHAYDVQQDExVleGFtcGxlLWVsbGlwdGljLXNoYTEwHhcNMTYwNjEwMjIx
NDEyWhcNMjMwNDE1MjIxNDEyWjBeMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0Ex
cGxlMR4wHAYDVQQDExVleGFtcGxlLWVsbGlwdGljLXNoYTEwHhcNMTYwNjIyMDAy
MzIwWhcNMjMwNDI3MDAyMzIwWjBeMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0Ex
EDAOBgNVBAoTB2NlcnRpZ28xEDAOBgNVBAsTB2V4YW1wbGUxHjAcBgNVBAMTFWV4
YW1wbGUtZWxsaXB0aWMtc2hhMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABM8V
3EeolNVmL+Hie/hVzYWucKMFH0LpJecnBaGnFRNz3WoL4fYe6aEKx2YRdTOyFSiC
DTZYyIU38sVDF3q5Hy2jTzBNMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD
ATAsBgNVHREEJTAjhwR/AAABhxAAAAAAAAAAAAAAAAAAAAABgglsb2NhbGhvc3Qw
CQYHKoZIzj0EAQNHADBEAiBeoQ8fJeQ8NVSvVWsA1sSjZX2JeF1AieqAKC/AlusM
uQIgM0iXusIK2OmatfJepYfZCJrXNnLi9piROfX0jvstreM=
YW1wbGUtZWxsaXB0aWMtc2hhMTCBmzAQBgcqhkjOPQIBBgUrgQQAIwOBhgAEARBY
2NPz8x6YNLA9UkPpPSoNM+JsLO6giyTuIwRy1j2PN94bmPRZcvffFMHo0jBbof/u
EEazJm0WAojmcqqED6caAMu+QcYrPdugHCHO1lv5iwZ5sJyswQ8qygaroMWBg7Qa
XjAs1p4Oncuh37CxRj70mse0YEPt+neBD5BM+8ZDchFXo08wTTAdBgNVHSUEFjAU
BggrBgEFBQcDAgYIKwYBBQUHAwEwLAYDVR0RBCUwI4cEfwAAAYcQAAAAAAAAAAAA
AAAAAAAAAYIJbG9jYWxob3N0MAkGByqGSM49BAEDgYwAMIGIAkIByNx56XB0CyNR
3vKIRdn9uoo8Q9PqBlO8ZtldeF+MqI/UvcnbrVxn7d5+lNj6K2YY2GzwV+IdxRyP
JSsHT03pww0CQgE5ssxuHZXCw1zLFXPilEHKpx1B3umRwR9q0lVc63d9zFT/Dz5L
QqF02WtegKS3Kr6M7R8fn2QvsdzOPqlcN57E2Q==
-----END CERTIFICATE-----
+9 -6
View File
@@ -1,8 +1,11 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIBGDCBwAIBADBeMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEDAOBgNVBAoT
B2NlcnRpZ28xEDAOBgNVBAsTB2V4YW1wbGUxHjAcBgNVBAMTFWV4YW1wbGUtZWxs
aXB0aWMtc2hhMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABM8V3EeolNVmL+Hi
e/hVzYWucKMFH0LpJecnBaGnFRNz3WoL4fYe6aEKx2YRdTOyFSiCDTZYyIU38sVD
F3q5Hy2gADAJBgcqhkjOPQQBA0gAMEUCIQDy8yncmMV6lW+WKrOnGC5CErrlo1TL
HaI9p2weexenLgIgLiO+4pINprkogDRDqPBow5qzTkyydxtiA+1npjjcYqY=
MIIBoTCCAQMCAQAwXjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQK
EwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMR4wHAYDVQQDExVleGFtcGxlLWVs
bGlwdGljLXNoYTEwgZswEAYHKoZIzj0CAQYFK4EEACMDgYYABAEQWNjT8/MemDSw
PVJD6T0qDTPibCzuoIsk7iMEctY9jzfeG5j0WXL33xTB6NIwW6H/7hBGsyZtFgKI
5nKqhA+nGgDLvkHGKz3boBwhztZb+YsGebCcrMEPKsoGq6DFgYO0Gl4wLNaeDp3L
od+wsUY+9JrHtGBD7fp3gQ+QTPvGQ3IRV6AAMAkGByqGSM49BAEDgYwAMIGIAkIA
57b3rNqca+NzuU1bX/vWV5bdFFN11YTmwjoWp8MHVd5AgZlVjYvVPVY531K6BS0c
HRB//5mAe99/8IfH7jsKPjcCQgFyEFjtXJmUkG3hieCT7rjYloFweBjkZbp9u5S5
J06NKwXNJO2kyhZYWBPQ/sTx+p6cPP1gk3GxTWIGUlg/W9siZg==
-----END CERTIFICATE REQUEST-----
Binary file not shown.
+6 -4
View File
@@ -1,8 +1,10 @@
-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
BgUrgQQAIw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIJ2Y2TSl0hYXHzCwS8H8EbgG7QwF8yShSsJq5UOrfcgXoAoGCCqGSM49
AwEHoUQDQgAEzxXcR6iU1WYv4eJ7+FXNha5wowUfQukl5ycFoacVE3Pdagvh9h7p
oQrHZhF1M7IVKIINNljIhTfyxUMXerkfLQ==
MIHcAgEBBEIBc4MT0rgcDEaimeP7Bxlhp3hAI2/KySFbOu6cg40taVXhXnaDAPqV
tKj3kru+1DhvxBCrUewzKo5u9g+OC7xdu8qgBwYFK4EEACOhgYkDgYYABAEQWNjT
8/MemDSwPVJD6T0qDTPibCzuoIsk7iMEctY9jzfeG5j0WXL33xTB6NIwW6H/7hBG
syZtFgKI5nKqhA+nGgDLvkHGKz3boBwhztZb+YsGebCcrMEPKsoGq6DFgYO0Gl4w
LNaeDp3Lod+wsUY+9JrHtGBD7fp3gQ+QTPvGQ3IRVw==
-----END EC PRIVATE KEY-----
Binary file not shown.