mirror of
https://github.com/square/certigo.git
synced 2024-04-21 12:32:40 +00:00
Merge pull request #76 from square/cs/ec-support
Support elliptic curve keys
This commit is contained in:
+37
-6
@@ -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
@@ -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")
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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-----
|
||||
|
||||
@@ -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.
@@ -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.
Reference in New Issue
Block a user