From b59e15c33d2745cad37c0791f747398afb4a6399 Mon Sep 17 00:00:00 2001 From: Andrew Stuart Date: Wed, 9 Dec 2015 12:49:36 -0700 Subject: [PATCH 01/19] Update ParsePEMBundle to properly handle pkcs#8 Implementation based on https://github.com/golang/go/blob/be16001187f17e9c312e69c353be743cc7d9e260/src/crypto/tls/tls.go#L273-L290 --- helper/certutil/helpers.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/helper/certutil/helpers.go b/helper/certutil/helpers.go index 64036ba44b..2facd2a387 100644 --- a/helper/certutil/helpers.go +++ b/helper/certutil/helpers.go @@ -134,7 +134,20 @@ func ParsePEMBundle(pemBundle string) (*ParsedCertBundle, error) { parsedBundle.PrivateKeyType = RSAPrivateKey parsedBundle.PrivateKeyBytes = pemBlock.Bytes parsedBundle.PrivateKey = signer - + } else if signer, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes); err == nil { + if parsedBundle.PrivateKeyType != UnknownPrivateKey { + return nil, UserError{"More than one private key given; provide only one private key in the bundle"} + } + switch signer := signer.(type) { + case *rsa.PrivateKey: + parsedBundle.PrivateKey = signer + parsedBundle.PrivateKeyType = RSAPrivateKey + parsedBundle.PrivateKeyBytes = pemBlock.Bytes + case *ecdsa.PrivateKey: + parsedBundle.PrivateKey = signer + parsedBundle.PrivateKeyType = ECPrivateKey + parsedBundle.PrivateKeyBytes = pemBlock.Bytes + } } else if certificates, err := x509.ParseCertificates(pemBlock.Bytes); err == nil { switch len(certificates) { case 0: From 5af21130d768b315ed85c4d7c8f6d74243539ad7 Mon Sep 17 00:00:00 2001 From: Andrew Stuart Date: Wed, 9 Dec 2015 13:41:32 -0700 Subject: [PATCH 02/19] Update tests and finish implementation of PKCS8 handling --- helper/certutil/certutil_test.go | 76 +++++++++++++++++++++++++++++--- helper/certutil/types.go | 32 ++++++++++++++ 2 files changed, 103 insertions(+), 5 deletions(-) diff --git a/helper/certutil/certutil_test.go b/helper/certutil/certutil_test.go index 66dc400566..2d03a42c12 100644 --- a/helper/certutil/certutil_test.go +++ b/helper/certutil/certutil_test.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/json" "fmt" + "log" "strings" "testing" @@ -18,18 +19,23 @@ import ( func TestCertBundleConversion(t *testing.T) { cbuts := []*CertBundle{ refreshRSACertBundle(), + refreshRSA8CertBundle(), refreshECCertBundle(), + refreshEC8CertBundle(), } - for _, cbut := range cbuts { + for i, cbut := range cbuts { pcbut, err := cbut.ToParsedCertBundle() if err != nil { - t.Fatalf("Error converting to parsed cert bundle: %s", err) + t.Logf("Error occurred with bundle %d in test array (index %d).\n", i+1, i) + t.Errorf("Error converting to parsed cert bundle: %s", err) + continue } err = compareCertBundleToParsedCertBundle(cbut, pcbut) if err != nil { - t.Fatalf(err.Error()) + t.Logf("Error occurred with bundle %d in test array (index %d).\n", i+1, i) + t.Errorf(err.Error()) } cbut, err := pcbut.ToCertBundle() @@ -108,10 +114,18 @@ func compareCertBundleToParsedCertBundle(cbut *CertBundle, pcbut *ParsedCertBund if pcbut.PrivateKeyType != RSAPrivateKey { return fmt.Errorf("Parsed bundle has wrong private key type") } + case privRSA8KeyPem: + if pcbut.PrivateKeyType != RSAPrivateKey { + return fmt.Errorf("Parsed bundle has wrong private key type") + } case privECKeyPem: if pcbut.PrivateKeyType != ECPrivateKey { return fmt.Errorf("Parsed bundle has wrong private key type") } + case privEC8KeyPem: + if pcbut.PrivateKeyType != ECPrivateKey { + return fmt.Errorf("Parsed bundle has wrong private key type") + } default: return fmt.Errorf("Parsed bundle has unknown private key type") } @@ -143,14 +157,15 @@ func compareCertBundleToParsedCertBundle(cbut *CertBundle, pcbut *ParsedCertBund if pcbut.PrivateKeyType != RSAPrivateKey { return fmt.Errorf("Bundle has wrong private key type") } - if cb.PrivateKey != privRSAKeyPem { + if cb.PrivateKey != privRSAKeyPem && cb.PrivateKey != privRSA8KeyPem { + log.Println(cb.PrivateKey, privRSAKeyPem, privRSA8KeyPem) return fmt.Errorf("Bundle private key does not match") } case "ec": if pcbut.PrivateKeyType != ECPrivateKey { return fmt.Errorf("Bundle has wrong private key type") } - if cb.PrivateKey != privECKeyPem { + if cb.PrivateKey != privECKeyPem && cb.PrivateKey != privEC8KeyPem { return fmt.Errorf("Bundle private key does not match") } default: @@ -326,6 +341,14 @@ func TestTLSConfig(t *testing.T) { } } +func refreshRSA8CertBundle() *CertBundle { + return &CertBundle{ + Certificate: certRSAPem, + PrivateKey: privRSA8KeyPem, + IssuingCA: issuingCaPem, + } +} + func refreshRSACertBundle() *CertBundle { ret := &CertBundle{ Certificate: certRSAPem, @@ -360,7 +383,44 @@ func refreshECCSRBundle() *CSRBundle { return ret } +func refreshEC8CertBundle() *CertBundle { + return &CertBundle{ + Certificate: certECPem, + PrivateKey: privEC8KeyPem, + IssuingCA: issuingCaPem, + } +} + const ( + privRSA8KeyPem = `-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC3dklRrO0JGJWD +zs/TLxPtAw0VCLrUEJIgp6nFddZzvOLkklIx9ACy1ckSoFiJKGxKlibqyiJPqFfT +vi3vBAAcZLf67uo2iBamZMBRNSc0gz5ALEfY1z+TNjFpZqO6lXOAa8t6KpTd3i0h +ST9mR+29YmvKvaFlzzMQ3cLikZL/YX5FD7M6/4GkAjUF1tAaEXub3a+fopL+Jayq +bAcb2gKGC9Z4EeNZQjyoZq4Fz6K3hLlHF83wkkgFdQhm1tqoVnOCO/yRQPKqhAa+ +Rj/gJ7UrRsUmwzvCYw+/7lCxwgEXgpaA3SRNIw89d+ef9AgB+FphCRP0yPAavr4H +dI4M0YJNAgMBAAECggEAe1LCGmsZs2GZL88XmKguxsWkR51kqSSydcz+rEN38rjn +9Cn/oqCYz54x2Zl7qkdH9CNW6cESq2VIFIfkrKSNxohVvBJZ0mpMf3F+bZhDUGNg +txaM/VBD5hspv+ZE7SmFSLAtSWPSSgoNYDCys3hqcUH1n4U1NxC/DPllBZRBsfSd +5C1Y7WcO8uxJyC7WRyGgTMkQloU5DX4d8Z5bEPvrp9nKplCLn5wuuE6oc4ZKU74g +VV7SGC6IQcWv0sQ9NdeRm05HjBN/uPVSzyzUpD+O/TWzH1LscRtkm6vMiNLbc7LA +RR0l3USwgTaUlXZBTBKICw9hk5JBmWA4bM9VEpWQaQKBgQDW76lU/av7FLFjLwpx +1xEJ0YYGHTRUvHXIlEdZnliMXnqGnRHRwb7EYtqvkoJ0GmviMmdAYivHyXDdIMK3 +gReXGAnGK1eUG2IoHIzv+ZhFF9RGv1YxGvjzwZCVYNujZdqWe5pzSqWQWpUiDQFp +b5nXkD8TUvr1pTFpHSjOUD0mXwKBgQDag0DkXai2rOj141i5w0COKFWEBN3XUMQJ +C9shCn/RsQl7RmefOr9AgYg5VqkLRUoYHAE/kO4svU/+dM+OMT9mGMW6Ew0zk/ML +qhsCMGH6AKlz5z7bVB6u/tEpROLawZPNEe6WlxxEN+4XxuHMPqUCjnQWlKY8T+i9 +nNv34ixe0wKBgFWdR0z0cqHjvzjrzvRDn6TSkdkzntm17BDGh5k6Crl3FMU0IZn0 +28EsQ0G2UUJgF+MVAq3RrPC627spRoaD5FqqF5KZRxxWwAWMQdOBD1dOQ58erf2H +aezmiGoIF9UBSE2y1HXiIQrcGhVjKtHNw3DrI0TWQ+K/N2xQUiXELmdvAoGANRSN +PuxBf56hOJnxg66aj+3cWCWWfidwd4IZyPzz78xBsWB464Up0FGm9cbHaaV7SkAD +TZ23Pcb/F6DoinIMJJD/9yOJoW3fLIY16WI3arOedjlGW6Ejkv7zcEL7mIhNjxM8 +EfjDNQ8hF0WItETDcMuKB7I0b5I5x1XDWYPno2ECgYEAsWHewaSG8+Ij8b+L/m0Z +lUD91L/gNVc6gdbjf5kMdYTCqI3q9N/9VWJyb7yRx8tjUTl9J//h7uYhCrujmpWf +1jcdaxqNLUUV7OcmM+PglprUe96A1zJwDOxc5DvHLbf/zBS6mA14PWYV1IUJdDdR +52wm5UEewSU9zlbvirgXj4U= +-----END PRIVATE KEY-----` + privRSAKeyPem = `-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAt3ZJUaztCRiVg87P0y8T7QMNFQi61BCSIKepxXXWc7zi5JJS MfQAstXJEqBYiShsSpYm6soiT6hX074t7wQAHGS3+u7qNogWpmTAUTUnNIM+QCxH @@ -445,6 +505,12 @@ wvvgOeCBovN3tSuGKzTiUKAAMAoGCCqGSM49BAMCAz8AMDwCHFap/5XDuqtXCG1g ljbYH5OWGBqGYCfL2k2+/6cCHAuk1bmOkGx7JAq/fSPd09i0DQIqUu7WHQHms48= -----END CERTIFICATE REQUEST-----` + privEC8KeyPem = `-----BEGIN PRIVATE KEY----- +MHgCAQAwEAYHKoZIzj0CAQYFK4EEACEEYTBfAgEBBBzN57mC5a72sATfYRlXLvZq +WghK+yzHuOGu6EDsoTwDOgAEwWd1V8ARPaHkkRKTOW9uT1ulori2+BF1qSmco4+e +dkGCmHu0AbhnvcL74DnggaLzd7Urhis04lA= +-----END PRIVATE KEY-----` + certECPem = `-----BEGIN CERTIFICATE----- MIIDJDCCAg6gAwIBAgIUM3J02tw0ZvpHUVHv6t8kcoft2/MwCwYJKoZIhvcNAQEL MBsxGTAXBgNVBAMMEFZhdWx0IFRlc3RpbmcgQ0EwHhcNMTUwNjE5MTcyODQyWhcN diff --git a/helper/certutil/types.go b/helper/certutil/types.go index 80209b5266..d25a7de85d 100644 --- a/helper/certutil/types.go +++ b/helper/certutil/types.go @@ -10,6 +10,8 @@ package certutil import ( "crypto" + "crypto/ecdsa" + "crypto/rsa" "crypto/tls" "crypto/x509" "encoding/pem" @@ -84,6 +86,7 @@ type CertBundle struct { // and a DER-encoded certificate type ParsedCertBundle struct { PrivateKeyType int + PKCS8 bool PrivateKeyBytes []byte PrivateKey crypto.Signer IssuingCABytes []byte @@ -137,6 +140,17 @@ func (c *CertBundle) ToParsedCertBundle() (*ParsedCertBundle, error) { } else if _, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes); err == nil { result.PrivateKeyType = RSAPrivateKey c.PrivateKeyType = "rsa" + } else if k, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes); err == nil { + result.PKCS8 = true + + switch k.(type) { + case *ecdsa.PrivateKey: + result.PrivateKeyType = ECPrivateKey + c.PrivateKeyType = "ec" + case *rsa.PrivateKey: + result.PrivateKeyType = RSAPrivateKey + c.PrivateKeyType = "rsa" + } } else { return nil, UserError{fmt.Sprintf("Unknown private key type in bundle: %s", c.PrivateKeyType)} } @@ -213,6 +227,9 @@ func (p *ParsedCertBundle) ToCertBundle() (*CertBundle, error) { default: return nil, InternalError{"Could not determine private key type when creating block"} } + if p.PKCS8 { + block.Type = "PRIVATE KEY" + } result.PrivateKey = strings.TrimSpace(string(pem.EncodeToMemory(&block))) } @@ -231,6 +248,21 @@ func (p *ParsedCertBundle) getSigner() (crypto.Signer, error) { return nil, UserError{"Given parsed cert bundle does not have private key information"} } + if p.PKCS8 { + if k, err := x509.ParsePKCS8PrivateKey(p.PrivateKeyBytes); err == nil { + switch k := k.(type) { + case *rsa.PrivateKey: + return k, nil + case *ecdsa.PrivateKey: + return k, nil + default: + return nil, UserError{fmt.Sprintf("Unable to determine pkcs8 key type")} + } + } else { + return nil, UserError{fmt.Sprintf("Error decoding pkcs8: %v", err)} + } + } + switch p.PrivateKeyType { case ECPrivateKey: signer, err = x509.ParseECPrivateKey(p.PrivateKeyBytes) From c8d49c2d6695ae19a7e9989ae7620f91ea8b6037 Mon Sep 17 00:00:00 2001 From: Andrew Stuart Date: Wed, 9 Dec 2015 13:52:55 -0700 Subject: [PATCH 03/19] Add pkcs8 flag setting in ParsePEMBundle --- helper/certutil/helpers.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/helper/certutil/helpers.go b/helper/certutil/helpers.go index 2facd2a387..1553a46aad 100644 --- a/helper/certutil/helpers.go +++ b/helper/certutil/helpers.go @@ -135,6 +135,8 @@ func ParsePEMBundle(pemBundle string) (*ParsedCertBundle, error) { parsedBundle.PrivateKeyBytes = pemBlock.Bytes parsedBundle.PrivateKey = signer } else if signer, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes); err == nil { + parsedBundle.PKCS8 = true + if parsedBundle.PrivateKeyType != UnknownPrivateKey { return nil, UserError{"More than one private key given; provide only one private key in the bundle"} } From 50b7be1c9afa9c702800de732f88b1339851b293 Mon Sep 17 00:00:00 2001 From: Andrew Stuart Date: Wed, 9 Dec 2015 14:55:03 -0700 Subject: [PATCH 04/19] Remove flag check before trying pkcs8 parsing. --- helper/certutil/types.go | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/helper/certutil/types.go b/helper/certutil/types.go index d25a7de85d..2b5c852a96 100644 --- a/helper/certutil/types.go +++ b/helper/certutil/types.go @@ -248,18 +248,12 @@ func (p *ParsedCertBundle) getSigner() (crypto.Signer, error) { return nil, UserError{"Given parsed cert bundle does not have private key information"} } - if p.PKCS8 { - if k, err := x509.ParsePKCS8PrivateKey(p.PrivateKeyBytes); err == nil { - switch k := k.(type) { - case *rsa.PrivateKey: - return k, nil - case *ecdsa.PrivateKey: - return k, nil - default: - return nil, UserError{fmt.Sprintf("Unable to determine pkcs8 key type")} - } - } else { - return nil, UserError{fmt.Sprintf("Error decoding pkcs8: %v", err)} + if k, err := x509.ParsePKCS8PrivateKey(p.PrivateKeyBytes); err == nil { + switch k := k.(type) { + case *rsa.PrivateKey: + return k, nil + case *ecdsa.PrivateKey: + return k, nil } } From 7bba342ee30e1d085f4b0084a7d2bfa5e285da83 Mon Sep 17 00:00:00 2001 From: Andrew Stuart Date: Wed, 9 Dec 2015 14:55:03 -0700 Subject: [PATCH 05/19] Remove flag check before trying pkcs8 parsing. --- helper/certutil/types.go | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/helper/certutil/types.go b/helper/certutil/types.go index d25a7de85d..2b5c852a96 100644 --- a/helper/certutil/types.go +++ b/helper/certutil/types.go @@ -248,18 +248,12 @@ func (p *ParsedCertBundle) getSigner() (crypto.Signer, error) { return nil, UserError{"Given parsed cert bundle does not have private key information"} } - if p.PKCS8 { - if k, err := x509.ParsePKCS8PrivateKey(p.PrivateKeyBytes); err == nil { - switch k := k.(type) { - case *rsa.PrivateKey: - return k, nil - case *ecdsa.PrivateKey: - return k, nil - default: - return nil, UserError{fmt.Sprintf("Unable to determine pkcs8 key type")} - } - } else { - return nil, UserError{fmt.Sprintf("Error decoding pkcs8: %v", err)} + if k, err := x509.ParsePKCS8PrivateKey(p.PrivateKeyBytes); err == nil { + switch k := k.(type) { + case *rsa.PrivateKey: + return k, nil + case *ecdsa.PrivateKey: + return k, nil } } From a9723189a15d409f40addf533ae421d1e0bc7174 Mon Sep 17 00:00:00 2001 From: Andrew Stuart Date: Thu, 10 Dec 2015 16:33:42 -0700 Subject: [PATCH 07/19] Remove debugging print statement in compareCertBundleToParsedCertBundle --- helper/certutil/certutil_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/helper/certutil/certutil_test.go b/helper/certutil/certutil_test.go index 2d03a42c12..0673bfc26b 100644 --- a/helper/certutil/certutil_test.go +++ b/helper/certutil/certutil_test.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/json" "fmt" - "log" "strings" "testing" @@ -158,7 +157,6 @@ func compareCertBundleToParsedCertBundle(cbut *CertBundle, pcbut *ParsedCertBund return fmt.Errorf("Bundle has wrong private key type") } if cb.PrivateKey != privRSAKeyPem && cb.PrivateKey != privRSA8KeyPem { - log.Println(cb.PrivateKey, privRSAKeyPem, privRSA8KeyPem) return fmt.Errorf("Bundle private key does not match") } case "ec": From ceb74f956c95fd5bfb8e475c28b9c8c1bb9292cc Mon Sep 17 00:00:00 2001 From: Andrew Stuart Date: Thu, 10 Dec 2015 21:00:17 -0700 Subject: [PATCH 08/19] Update flag to field with format info --- helper/certutil/helpers.go | 4 ++- helper/certutil/types.go | 55 +++++++++++++++++++++++++++----------- 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/helper/certutil/helpers.go b/helper/certutil/helpers.go index 1553a46aad..aaa5945eee 100644 --- a/helper/certutil/helpers.go +++ b/helper/certutil/helpers.go @@ -123,6 +123,7 @@ func ParsePEMBundle(pemBundle string) (*ParsedCertBundle, error) { if parsedBundle.PrivateKeyType != UnknownPrivateKey { return nil, UserError{"more than one private key given; provide only one private key in the bundle"} } + parsedBundle.PrivateKeyFormat = EC parsedBundle.PrivateKeyType = ECPrivateKey parsedBundle.PrivateKeyBytes = pemBlock.Bytes parsedBundle.PrivateKey = signer @@ -132,10 +133,11 @@ func ParsePEMBundle(pemBundle string) (*ParsedCertBundle, error) { return nil, UserError{"more than one private key given; provide only one private key in the bundle"} } parsedBundle.PrivateKeyType = RSAPrivateKey + parsedBundle.PrivateKeyFormat = PKCS1 parsedBundle.PrivateKeyBytes = pemBlock.Bytes parsedBundle.PrivateKey = signer } else if signer, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes); err == nil { - parsedBundle.PKCS8 = true + parsedBundle.PrivateKeyFormat = PKCS8 if parsedBundle.PrivateKeyType != UnknownPrivateKey { return nil, UserError{"More than one private key given; provide only one private key in the bundle"} diff --git a/helper/certutil/types.go b/helper/certutil/types.go index 2b5c852a96..6e76538d11 100644 --- a/helper/certutil/types.go +++ b/helper/certutil/types.go @@ -47,6 +47,16 @@ const ( TLSClient ) +//KeyFormat indicates the serialization format of the key +type KeyFormat string + +//Well-known formats +const ( + PKCS1 KeyFormat = "pkcs1" + PKCS8 KeyFormat = "pkcs8" + EC KeyFormat = "ec" +) + // UserError represents an error generated due to invalid user input type UserError struct { Err string @@ -66,7 +76,7 @@ func (e InternalError) Error() string { return e.Err } -// Used to allow common key setting for certs and CSRs +//ParsedPrivateKeyContainer allows common key setting for certs and CSRs type ParsedPrivateKeyContainer interface { SetParsedPrivateKey(crypto.Signer, int, []byte) } @@ -75,18 +85,19 @@ type ParsedPrivateKeyContainer interface { // a PEM-encoded certificate, and a string-encoded serial number, // returned from a successful Issue request type CertBundle struct { - PrivateKeyType string `json:"private_key_type" structs:"private_key_type" mapstructure:"private_key_type"` - Certificate string `json:"certificate" structs:"certificate" mapstructure:"certificate"` - IssuingCA string `json:"issuing_ca" structs:"issuing_ca" mapstructure:"issuing_ca"` - PrivateKey string `json:"private_key" structs:"private_key" mapstructure:"private_key"` - SerialNumber string `json:"serial_number" structs:"serial_number" mapstructure:"serial_number"` + PrivateKeyType string `json:"private_key_type" structs:"private_key_type" mapstructure:"private_key_type"` + PrivateKeyFormat KeyFormat `json:"private_key_format" structs:"private_key_format" mapstructure:"private_key_format"` + Certificate string `json:"certificate" structs:"certificate" mapstructure:"certificate"` + IssuingCA string `json:"issuing_ca" structs:"issuing_ca" mapstructure:"issuing_ca"` + PrivateKey string `json:"private_key" structs:"private_key" mapstructure:"private_key"` + SerialNumber string `json:"serial_number" structs:"serial_number" mapstructure:"serial_number"` } // ParsedCertBundle contains a key type, a DER-encoded private key, // and a DER-encoded certificate type ParsedCertBundle struct { PrivateKeyType int - PKCS8 bool + PrivateKeyFormat KeyFormat PrivateKeyBytes []byte PrivateKey crypto.Signer IssuingCABytes []byte @@ -126,6 +137,7 @@ func (c *CertBundle) ToParsedCertBundle() (*ParsedCertBundle, error) { return nil, UserError{"Error decoding private key from cert bundle"} } result.PrivateKeyBytes = pemBlock.Bytes + result.PrivateKeyFormat = c.PrivateKeyFormat switch c.PrivateKeyType { case "ec": @@ -135,13 +147,20 @@ func (c *CertBundle) ToParsedCertBundle() (*ParsedCertBundle, error) { default: // Try to figure it out and correct if _, err := x509.ParseECPrivateKey(pemBlock.Bytes); err == nil { + result.PrivateKeyFormat = EC + c.PrivateKeyFormat = EC + result.PrivateKeyType = ECPrivateKey c.PrivateKeyType = "ec" } else if _, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes); err == nil { + result.PrivateKeyFormat = PKCS1 + c.PrivateKeyFormat = PKCS1 + result.PrivateKeyType = RSAPrivateKey c.PrivateKeyType = "rsa" } else if k, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes); err == nil { - result.PKCS8 = true + result.PrivateKeyFormat = PKCS8 + c.PrivateKeyFormat = PKCS8 switch k.(type) { case *ecdsa.PrivateKey: @@ -216,6 +235,7 @@ func (p *ParsedCertBundle) ToCertBundle() (*CertBundle, error) { } if p.PrivateKeyBytes != nil && len(p.PrivateKeyBytes) > 0 { + result.PrivateKeyFormat = p.PrivateKeyFormat block.Bytes = p.PrivateKeyBytes switch p.PrivateKeyType { case RSAPrivateKey: @@ -227,7 +247,7 @@ func (p *ParsedCertBundle) ToCertBundle() (*CertBundle, error) { default: return nil, InternalError{"Could not determine private key type when creating block"} } - if p.PKCS8 { + if p.PrivateKeyFormat == PKCS8 { block.Type = "PRIVATE KEY" } result.PrivateKey = strings.TrimSpace(string(pem.EncodeToMemory(&block))) @@ -248,13 +268,18 @@ func (p *ParsedCertBundle) getSigner() (crypto.Signer, error) { return nil, UserError{"Given parsed cert bundle does not have private key information"} } - if k, err := x509.ParsePKCS8PrivateKey(p.PrivateKeyBytes); err == nil { - switch k := k.(type) { - case *rsa.PrivateKey: - return k, nil - case *ecdsa.PrivateKey: - return k, nil + if p.PrivateKeyFormat == PKCS8 { + if k, err := x509.ParsePKCS8PrivateKey(p.PrivateKeyBytes); err == nil { + switch k := k.(type) { + case *rsa.PrivateKey: + return k, nil + case *ecdsa.PrivateKey: + return k, nil + default: + return nil, UserError{"Found unknown private key type in pkcs#8 wrapping"} + } } + return nil, UserError{fmt.Sprintf("Failed to parse pkcs#8 key: %v", err)} } switch p.PrivateKeyType { From ee563cdc32e850d8ad9c9be0c37708c34416211d Mon Sep 17 00:00:00 2001 From: Andrew Stuart Date: Fri, 11 Dec 2015 09:58:49 -0700 Subject: [PATCH 09/19] Add benchmark for certutil bundle parsing --- helper/certutil/certutil_test.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/helper/certutil/certutil_test.go b/helper/certutil/certutil_test.go index 0673bfc26b..dc0721b146 100644 --- a/helper/certutil/certutil_test.go +++ b/helper/certutil/certutil_test.go @@ -49,6 +49,31 @@ func TestCertBundleConversion(t *testing.T) { } } +func BenchmarkCertBundleParsing(b *testing.B) { + for i := 0; i < b.N; i++ { + cbuts := []*CertBundle{ + refreshRSACertBundle(), + refreshRSA8CertBundle(), + refreshECCertBundle(), + refreshEC8CertBundle(), + } + + for i, cbut := range cbuts { + pcbut, err := cbut.ToParsedCertBundle() + if err != nil { + b.Logf("Error occurred with bundle %d in test array (index %d).\n", i+1, i) + b.Errorf("Error converting to parsed cert bundle: %s", err) + continue + } + + cbut, err = pcbut.ToCertBundle() + if err != nil { + b.Fatalf("Error converting to cert bundle: %s", err) + } + } + } +} + func TestCertBundleParsing(t *testing.T) { jsonBundle := refreshRSACertBundle() jsonString, err := json.Marshal(jsonBundle) From dfc052a755ab057c2339c68d7e3f0c62cf3c733c Mon Sep 17 00:00:00 2001 From: Andrew Stuart Date: Fri, 11 Dec 2015 13:43:14 -0700 Subject: [PATCH 10/19] Move to pem.Block.Type-based decoding --- helper/certutil/certutil_test.go | 23 ++--- helper/certutil/helpers.go | 8 +- helper/certutil/types.go | 168 +++++++++++++++---------------- 3 files changed, 94 insertions(+), 105 deletions(-) diff --git a/helper/certutil/certutil_test.go b/helper/certutil/certutil_test.go index dc0721b146..e12b0b7159 100644 --- a/helper/certutil/certutil_test.go +++ b/helper/certutil/certutil_test.go @@ -136,19 +136,19 @@ func compareCertBundleToParsedCertBundle(cbut *CertBundle, pcbut *ParsedCertBund switch cbut.PrivateKey { case privRSAKeyPem: if pcbut.PrivateKeyType != RSAPrivateKey { - return fmt.Errorf("Parsed bundle has wrong private key type") + return fmt.Errorf("Parsed bundle has wrong private key type: %v, should be 'rsa' (%v)", pcbut.PrivateKeyType, RSAPrivateKey) } case privRSA8KeyPem: if pcbut.PrivateKeyType != RSAPrivateKey { - return fmt.Errorf("Parsed bundle has wrong private key type") + return fmt.Errorf("Parsed bundle has wrong pkcs8 private key type: %v, should be 'rsa' (%v)", pcbut.PrivateKeyType, RSAPrivateKey) } case privECKeyPem: if pcbut.PrivateKeyType != ECPrivateKey { - return fmt.Errorf("Parsed bundle has wrong private key type") + return fmt.Errorf("Parsed bundle has wrong private key type: %v, should be 'ec' (%v)", pcbut.PrivateKeyType, ECPrivateKey) } case privEC8KeyPem: if pcbut.PrivateKeyType != ECPrivateKey { - return fmt.Errorf("Parsed bundle has wrong private key type") + return fmt.Errorf("Parsed bundle has wrong pkcs8 private key type: %v, should be 'ec' (%v)", pcbut.PrivateKeyType, ECPrivateKey) } default: return fmt.Errorf("Parsed bundle has unknown private key type") @@ -176,23 +176,17 @@ func compareCertBundleToParsedCertBundle(cbut *CertBundle, pcbut *ParsedCertBund return fmt.Errorf("Bundle has nil issuing CA") } - switch cb.PrivateKeyType { - case "rsa": - if pcbut.PrivateKeyType != RSAPrivateKey { - return fmt.Errorf("Bundle has wrong private key type") - } + switch pcbut.PrivateKeyType { + case RSAPrivateKey: if cb.PrivateKey != privRSAKeyPem && cb.PrivateKey != privRSA8KeyPem { return fmt.Errorf("Bundle private key does not match") } - case "ec": - if pcbut.PrivateKeyType != ECPrivateKey { - return fmt.Errorf("Bundle has wrong private key type") - } + case ECPrivateKey: if cb.PrivateKey != privECKeyPem && cb.PrivateKey != privEC8KeyPem { return fmt.Errorf("Bundle private key does not match") } default: - return fmt.Errorf("Bundle has unknown private key type") + return fmt.Errorf("CertBundle has unknown private key type") } if cb.SerialNumber != GetOctalFormatted(pcbut.Certificate.SerialNumber.Bytes(), ":") { @@ -287,6 +281,7 @@ func compareCSRBundleToParsedCSRBundle(csrbut *CSRBundle, pcsrbut *ParsedCSRBund return fmt.Errorf("Bundle private key does not match") } default: + fmt.Printf("csrb = %+v\n", csrb) return fmt.Errorf("Bundle has unknown private key type") } diff --git a/helper/certutil/helpers.go b/helper/certutil/helpers.go index aaa5945eee..eeb0f8074a 100644 --- a/helper/certutil/helpers.go +++ b/helper/certutil/helpers.go @@ -123,7 +123,7 @@ func ParsePEMBundle(pemBundle string) (*ParsedCertBundle, error) { if parsedBundle.PrivateKeyType != UnknownPrivateKey { return nil, UserError{"more than one private key given; provide only one private key in the bundle"} } - parsedBundle.PrivateKeyFormat = EC + parsedBundle.PrivateKeyFormat = ECBlock parsedBundle.PrivateKeyType = ECPrivateKey parsedBundle.PrivateKeyBytes = pemBlock.Bytes parsedBundle.PrivateKey = signer @@ -133,11 +133,11 @@ func ParsePEMBundle(pemBundle string) (*ParsedCertBundle, error) { return nil, UserError{"more than one private key given; provide only one private key in the bundle"} } parsedBundle.PrivateKeyType = RSAPrivateKey - parsedBundle.PrivateKeyFormat = PKCS1 + parsedBundle.PrivateKeyFormat = PKCS1Block parsedBundle.PrivateKeyBytes = pemBlock.Bytes parsedBundle.PrivateKey = signer } else if signer, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes); err == nil { - parsedBundle.PrivateKeyFormat = PKCS8 + parsedBundle.PrivateKeyFormat = PKCS8Block if parsedBundle.PrivateKeyType != UnknownPrivateKey { return nil, UserError{"More than one private key given; provide only one private key in the bundle"} @@ -203,7 +203,7 @@ func ParsePEMBundle(pemBundle string) (*ParsedCertBundle, error) { // GeneratePrivateKey generates a private key with the specified type and key bits func GeneratePrivateKey(keyType string, keyBits int, container ParsedPrivateKeyContainer) error { var err error - var privateKeyType int + var privateKeyType PrivateKeyType var privateKeyBytes []byte var privateKey crypto.Signer diff --git a/helper/certutil/types.go b/helper/certutil/types.go index 6e76538d11..dfa9b4614c 100644 --- a/helper/certutil/types.go +++ b/helper/certutil/types.go @@ -3,9 +3,7 @@ // includes helpers for converting a certificate/private key bundle // between DER and PEM, printing certificate serial numbers, and more. // -// Functionality specific to the PKI backend includes some types -// and helper methods to make requesting certificates from the -// backend easy. +// Functionality specific to the PKI backend includes some types // and helper methods to make requesting certificates from the // backend easy. package certutil import ( @@ -31,7 +29,7 @@ type Secret struct { type PrivateKeyType int const ( - UnknownPrivateKey = iota + UnknownPrivateKey = PrivateKeyType(iota) RSAPrivateKey ECPrivateKey ) @@ -47,14 +45,14 @@ const ( TLSClient ) -//KeyFormat indicates the serialization format of the key -type KeyFormat string +//BlockType indicates the serialization format of the key +type BlockType string //Well-known formats const ( - PKCS1 KeyFormat = "pkcs1" - PKCS8 KeyFormat = "pkcs8" - EC KeyFormat = "ec" + PKCS1Block BlockType = "RSA PRIVATE KEY" + PKCS8Block BlockType = "PRIVATE KEY" + ECBlock BlockType = "EC PRIVATE KEY" ) // UserError represents an error generated due to invalid user input @@ -78,26 +76,25 @@ func (e InternalError) Error() string { //ParsedPrivateKeyContainer allows common key setting for certs and CSRs type ParsedPrivateKeyContainer interface { - SetParsedPrivateKey(crypto.Signer, int, []byte) + SetParsedPrivateKey(crypto.Signer, PrivateKeyType, []byte) } // CertBundle contains a key type, a PEM-encoded private key, // a PEM-encoded certificate, and a string-encoded serial number, // returned from a successful Issue request type CertBundle struct { - PrivateKeyType string `json:"private_key_type" structs:"private_key_type" mapstructure:"private_key_type"` - PrivateKeyFormat KeyFormat `json:"private_key_format" structs:"private_key_format" mapstructure:"private_key_format"` - Certificate string `json:"certificate" structs:"certificate" mapstructure:"certificate"` - IssuingCA string `json:"issuing_ca" structs:"issuing_ca" mapstructure:"issuing_ca"` - PrivateKey string `json:"private_key" structs:"private_key" mapstructure:"private_key"` - SerialNumber string `json:"serial_number" structs:"serial_number" mapstructure:"serial_number"` + PrivateKeyType string `json:"private_key_type" structs:"private_key_type" mapstructure:"private_key_type"` + Certificate string `json:"certificate" structs:"certificate" mapstructure:"certificate"` + IssuingCA string `json:"issuing_ca" structs:"issuing_ca" mapstructure:"issuing_ca"` + PrivateKey string `json:"private_key" structs:"private_key" mapstructure:"private_key"` + SerialNumber string `json:"serial_number" structs:"serial_number" mapstructure:"serial_number"` } // ParsedCertBundle contains a key type, a DER-encoded private key, // and a DER-encoded certificate type ParsedCertBundle struct { - PrivateKeyType int - PrivateKeyFormat KeyFormat + PrivateKeyType PrivateKeyType + PrivateKeyFormat BlockType PrivateKeyBytes []byte PrivateKey crypto.Signer IssuingCABytes []byte @@ -117,7 +114,7 @@ type CSRBundle struct { // ParsedCSRBundle contains a key type, a DER-encoded private key, // and a DER-encoded certificate request type ParsedCSRBundle struct { - PrivateKeyType int + PrivateKeyType PrivateKeyType PrivateKeyBytes []byte PrivateKey crypto.Signer CSRBytes []byte @@ -136,43 +133,31 @@ func (c *CertBundle) ToParsedCertBundle() (*ParsedCertBundle, error) { if pemBlock == nil { return nil, UserError{"Error decoding private key from cert bundle"} } + result.PrivateKeyBytes = pemBlock.Bytes - result.PrivateKeyFormat = c.PrivateKeyFormat + result.PrivateKeyFormat = BlockType(strings.TrimSpace(pemBlock.Type)) - switch c.PrivateKeyType { - case "ec": + switch result.PrivateKeyFormat { + case ECBlock: + c.PrivateKeyType = "ec" result.PrivateKeyType = ECPrivateKey - case "rsa": + case PKCS1Block: + c.PrivateKeyType = "rsa" result.PrivateKeyType = RSAPrivateKey - default: - // Try to figure it out and correct - if _, err := x509.ParseECPrivateKey(pemBlock.Bytes); err == nil { - result.PrivateKeyFormat = EC - c.PrivateKeyFormat = EC - - result.PrivateKeyType = ECPrivateKey - c.PrivateKeyType = "ec" - } else if _, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes); err == nil { - result.PrivateKeyFormat = PKCS1 - c.PrivateKeyFormat = PKCS1 - - result.PrivateKeyType = RSAPrivateKey - c.PrivateKeyType = "rsa" - } else if k, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes); err == nil { - result.PrivateKeyFormat = PKCS8 - c.PrivateKeyFormat = PKCS8 - - switch k.(type) { - case *ecdsa.PrivateKey: - result.PrivateKeyType = ECPrivateKey - c.PrivateKeyType = "ec" - case *rsa.PrivateKey: - result.PrivateKeyType = RSAPrivateKey - c.PrivateKeyType = "rsa" - } - } else { - return nil, UserError{fmt.Sprintf("Unknown private key type in bundle: %s", c.PrivateKeyType)} + case PKCS8Block: + t, err := getPKCS8Type(pemBlock.Bytes) + if err != nil { + return nil, UserError{fmt.Sprintf("Error getting key type from pkcs#8: %v", err)} } + result.PrivateKeyType = t + switch t { + case ECPrivateKey: + c.PrivateKeyType = "ec" + case RSAPrivateKey: + c.PrivateKeyType = "rsa" + } + default: + return nil, UserError{fmt.Sprintf("Unsupported key block type: %s", pemBlock.Type)} } result.PrivateKey, err = result.getSigner() @@ -235,20 +220,17 @@ func (p *ParsedCertBundle) ToCertBundle() (*CertBundle, error) { } if p.PrivateKeyBytes != nil && len(p.PrivateKeyBytes) > 0 { - result.PrivateKeyFormat = p.PrivateKeyFormat + block.Type = string(p.PrivateKeyFormat) block.Bytes = p.PrivateKeyBytes - switch p.PrivateKeyType { - case RSAPrivateKey: - result.PrivateKeyType = "rsa" - block.Type = "RSA PRIVATE KEY" - case ECPrivateKey: - result.PrivateKeyType = "ec" - block.Type = "EC PRIVATE KEY" - default: - return nil, InternalError{"Could not determine private key type when creating block"} - } - if p.PrivateKeyFormat == PKCS8 { - block.Type = "PRIVATE KEY" + + //Handle bundle not parsed by us + if block.Type == "" { + switch p.PrivateKeyType { + case ECPrivateKey: + block.Type = string(ECBlock) + case RSAPrivateKey: + block.Type = string(PKCS1Block) + } } result.PrivateKey = strings.TrimSpace(string(pem.EncodeToMemory(&block))) } @@ -268,33 +250,29 @@ func (p *ParsedCertBundle) getSigner() (crypto.Signer, error) { return nil, UserError{"Given parsed cert bundle does not have private key information"} } - if p.PrivateKeyFormat == PKCS8 { - if k, err := x509.ParsePKCS8PrivateKey(p.PrivateKeyBytes); err == nil { - switch k := k.(type) { - case *rsa.PrivateKey: - return k, nil - case *ecdsa.PrivateKey: - return k, nil - default: - return nil, UserError{"Found unknown private key type in pkcs#8 wrapping"} - } - } - return nil, UserError{fmt.Sprintf("Failed to parse pkcs#8 key: %v", err)} - } - - switch p.PrivateKeyType { - case ECPrivateKey: + switch p.PrivateKeyFormat { + case ECBlock: signer, err = x509.ParseECPrivateKey(p.PrivateKeyBytes) if err != nil { return nil, UserError{fmt.Sprintf("Unable to parse CA's private EC key: %s", err)} } - case RSAPrivateKey: + case PKCS1Block: signer, err = x509.ParsePKCS1PrivateKey(p.PrivateKeyBytes) if err != nil { return nil, UserError{fmt.Sprintf("Unable to parse CA's private RSA key: %s", err)} } + case PKCS8Block: + if k, err := x509.ParsePKCS8PrivateKey(p.PrivateKeyBytes); err == nil { + switch k := k.(type) { + case *rsa.PrivateKey, *ecdsa.PrivateKey: + return k.(crypto.Signer), nil + default: + return nil, UserError{"Found unknown private key type in pkcs#8 wrapping"} + } + } + return nil, UserError{fmt.Sprintf("Failed to parse pkcs#8 key: %v", err)} default: return nil, UserError{"Unable to determine type of private key; only RSA and EC are supported"} } @@ -302,12 +280,28 @@ func (p *ParsedCertBundle) getSigner() (crypto.Signer, error) { } // SetParsedPrivateKey sets the private key parameters on the bundle -func (p *ParsedCertBundle) SetParsedPrivateKey(privateKey crypto.Signer, privateKeyType int, privateKeyBytes []byte) { +func (p *ParsedCertBundle) SetParsedPrivateKey(privateKey crypto.Signer, privateKeyType PrivateKeyType, privateKeyBytes []byte) { p.PrivateKey = privateKey - p.PrivateKeyType = privateKeyType + p.PrivateKeyType = PrivateKeyType(privateKeyType) p.PrivateKeyBytes = privateKeyBytes } +func getPKCS8Type(bs []byte) (PrivateKeyType, error) { + k, err := x509.ParsePKCS8PrivateKey(bs) + if err != nil { + return UnknownPrivateKey, UserError{fmt.Sprintf("Failed to parse pkcs#8 key: %v", err)} + } + + switch k.(type) { + case *ecdsa.PrivateKey: + return ECPrivateKey, nil + case *rsa.PrivateKey: + return RSAPrivateKey, nil + default: + return UnknownPrivateKey, UserError{"Found unknown private key type in pkcs#8 wrapping"} + } +} + // ToParsedCSRBundle converts a string-based CSR bundle // to a byte-based raw CSR bundle func (c *CSRBundle) ToParsedCSRBundle() (*ParsedCSRBundle, error) { @@ -322,10 +316,10 @@ func (c *CSRBundle) ToParsedCSRBundle() (*ParsedCSRBundle, error) { } result.PrivateKeyBytes = pemBlock.Bytes - switch c.PrivateKeyType { - case "ec": + switch BlockType(pemBlock.Type) { + case ECBlock: result.PrivateKeyType = ECPrivateKey - case "rsa": + case PKCS1Block: result.PrivateKeyType = RSAPrivateKey default: // Try to figure it out and correct @@ -424,7 +418,7 @@ func (p *ParsedCSRBundle) getSigner() (crypto.Signer, error) { } // SetParsedPrivateKey sets the private key parameters on the bundle -func (p *ParsedCSRBundle) SetParsedPrivateKey(privateKey crypto.Signer, privateKeyType int, privateKeyBytes []byte) { +func (p *ParsedCSRBundle) SetParsedPrivateKey(privateKey crypto.Signer, privateKeyType PrivateKeyType, privateKeyBytes []byte) { p.PrivateKey = privateKey p.PrivateKeyType = privateKeyType p.PrivateKeyBytes = privateKeyBytes From 39a3a92e799d9c4a50dc82a25d94eb42bc72a381 Mon Sep 17 00:00:00 2001 From: Andrew Stuart Date: Wed, 9 Dec 2015 12:49:36 -0700 Subject: [PATCH 11/19] Update ParsePEMBundle to properly handle pkcs#8 Implementation based on https://github.com/golang/go/blob/be16001187f17e9c312e69c353be743cc7d9e260/src/crypto/tls/tls.go#L273-L290 --- helper/certutil/helpers.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/helper/certutil/helpers.go b/helper/certutil/helpers.go index 64036ba44b..2facd2a387 100644 --- a/helper/certutil/helpers.go +++ b/helper/certutil/helpers.go @@ -134,7 +134,20 @@ func ParsePEMBundle(pemBundle string) (*ParsedCertBundle, error) { parsedBundle.PrivateKeyType = RSAPrivateKey parsedBundle.PrivateKeyBytes = pemBlock.Bytes parsedBundle.PrivateKey = signer - + } else if signer, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes); err == nil { + if parsedBundle.PrivateKeyType != UnknownPrivateKey { + return nil, UserError{"More than one private key given; provide only one private key in the bundle"} + } + switch signer := signer.(type) { + case *rsa.PrivateKey: + parsedBundle.PrivateKey = signer + parsedBundle.PrivateKeyType = RSAPrivateKey + parsedBundle.PrivateKeyBytes = pemBlock.Bytes + case *ecdsa.PrivateKey: + parsedBundle.PrivateKey = signer + parsedBundle.PrivateKeyType = ECPrivateKey + parsedBundle.PrivateKeyBytes = pemBlock.Bytes + } } else if certificates, err := x509.ParseCertificates(pemBlock.Bytes); err == nil { switch len(certificates) { case 0: From e38596fc1cfc6815e93376fdec911ef40f67bcd7 Mon Sep 17 00:00:00 2001 From: Andrew Stuart Date: Wed, 9 Dec 2015 13:41:32 -0700 Subject: [PATCH 12/19] Update tests and finish implementation of PKCS8 handling --- helper/certutil/certutil_test.go | 76 +++++++++++++++++++++++++++++--- helper/certutil/types.go | 32 ++++++++++++++ 2 files changed, 103 insertions(+), 5 deletions(-) diff --git a/helper/certutil/certutil_test.go b/helper/certutil/certutil_test.go index 66dc400566..2d03a42c12 100644 --- a/helper/certutil/certutil_test.go +++ b/helper/certutil/certutil_test.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/json" "fmt" + "log" "strings" "testing" @@ -18,18 +19,23 @@ import ( func TestCertBundleConversion(t *testing.T) { cbuts := []*CertBundle{ refreshRSACertBundle(), + refreshRSA8CertBundle(), refreshECCertBundle(), + refreshEC8CertBundle(), } - for _, cbut := range cbuts { + for i, cbut := range cbuts { pcbut, err := cbut.ToParsedCertBundle() if err != nil { - t.Fatalf("Error converting to parsed cert bundle: %s", err) + t.Logf("Error occurred with bundle %d in test array (index %d).\n", i+1, i) + t.Errorf("Error converting to parsed cert bundle: %s", err) + continue } err = compareCertBundleToParsedCertBundle(cbut, pcbut) if err != nil { - t.Fatalf(err.Error()) + t.Logf("Error occurred with bundle %d in test array (index %d).\n", i+1, i) + t.Errorf(err.Error()) } cbut, err := pcbut.ToCertBundle() @@ -108,10 +114,18 @@ func compareCertBundleToParsedCertBundle(cbut *CertBundle, pcbut *ParsedCertBund if pcbut.PrivateKeyType != RSAPrivateKey { return fmt.Errorf("Parsed bundle has wrong private key type") } + case privRSA8KeyPem: + if pcbut.PrivateKeyType != RSAPrivateKey { + return fmt.Errorf("Parsed bundle has wrong private key type") + } case privECKeyPem: if pcbut.PrivateKeyType != ECPrivateKey { return fmt.Errorf("Parsed bundle has wrong private key type") } + case privEC8KeyPem: + if pcbut.PrivateKeyType != ECPrivateKey { + return fmt.Errorf("Parsed bundle has wrong private key type") + } default: return fmt.Errorf("Parsed bundle has unknown private key type") } @@ -143,14 +157,15 @@ func compareCertBundleToParsedCertBundle(cbut *CertBundle, pcbut *ParsedCertBund if pcbut.PrivateKeyType != RSAPrivateKey { return fmt.Errorf("Bundle has wrong private key type") } - if cb.PrivateKey != privRSAKeyPem { + if cb.PrivateKey != privRSAKeyPem && cb.PrivateKey != privRSA8KeyPem { + log.Println(cb.PrivateKey, privRSAKeyPem, privRSA8KeyPem) return fmt.Errorf("Bundle private key does not match") } case "ec": if pcbut.PrivateKeyType != ECPrivateKey { return fmt.Errorf("Bundle has wrong private key type") } - if cb.PrivateKey != privECKeyPem { + if cb.PrivateKey != privECKeyPem && cb.PrivateKey != privEC8KeyPem { return fmt.Errorf("Bundle private key does not match") } default: @@ -326,6 +341,14 @@ func TestTLSConfig(t *testing.T) { } } +func refreshRSA8CertBundle() *CertBundle { + return &CertBundle{ + Certificate: certRSAPem, + PrivateKey: privRSA8KeyPem, + IssuingCA: issuingCaPem, + } +} + func refreshRSACertBundle() *CertBundle { ret := &CertBundle{ Certificate: certRSAPem, @@ -360,7 +383,44 @@ func refreshECCSRBundle() *CSRBundle { return ret } +func refreshEC8CertBundle() *CertBundle { + return &CertBundle{ + Certificate: certECPem, + PrivateKey: privEC8KeyPem, + IssuingCA: issuingCaPem, + } +} + const ( + privRSA8KeyPem = `-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC3dklRrO0JGJWD +zs/TLxPtAw0VCLrUEJIgp6nFddZzvOLkklIx9ACy1ckSoFiJKGxKlibqyiJPqFfT +vi3vBAAcZLf67uo2iBamZMBRNSc0gz5ALEfY1z+TNjFpZqO6lXOAa8t6KpTd3i0h +ST9mR+29YmvKvaFlzzMQ3cLikZL/YX5FD7M6/4GkAjUF1tAaEXub3a+fopL+Jayq +bAcb2gKGC9Z4EeNZQjyoZq4Fz6K3hLlHF83wkkgFdQhm1tqoVnOCO/yRQPKqhAa+ +Rj/gJ7UrRsUmwzvCYw+/7lCxwgEXgpaA3SRNIw89d+ef9AgB+FphCRP0yPAavr4H +dI4M0YJNAgMBAAECggEAe1LCGmsZs2GZL88XmKguxsWkR51kqSSydcz+rEN38rjn +9Cn/oqCYz54x2Zl7qkdH9CNW6cESq2VIFIfkrKSNxohVvBJZ0mpMf3F+bZhDUGNg +txaM/VBD5hspv+ZE7SmFSLAtSWPSSgoNYDCys3hqcUH1n4U1NxC/DPllBZRBsfSd +5C1Y7WcO8uxJyC7WRyGgTMkQloU5DX4d8Z5bEPvrp9nKplCLn5wuuE6oc4ZKU74g +VV7SGC6IQcWv0sQ9NdeRm05HjBN/uPVSzyzUpD+O/TWzH1LscRtkm6vMiNLbc7LA +RR0l3USwgTaUlXZBTBKICw9hk5JBmWA4bM9VEpWQaQKBgQDW76lU/av7FLFjLwpx +1xEJ0YYGHTRUvHXIlEdZnliMXnqGnRHRwb7EYtqvkoJ0GmviMmdAYivHyXDdIMK3 +gReXGAnGK1eUG2IoHIzv+ZhFF9RGv1YxGvjzwZCVYNujZdqWe5pzSqWQWpUiDQFp +b5nXkD8TUvr1pTFpHSjOUD0mXwKBgQDag0DkXai2rOj141i5w0COKFWEBN3XUMQJ +C9shCn/RsQl7RmefOr9AgYg5VqkLRUoYHAE/kO4svU/+dM+OMT9mGMW6Ew0zk/ML +qhsCMGH6AKlz5z7bVB6u/tEpROLawZPNEe6WlxxEN+4XxuHMPqUCjnQWlKY8T+i9 +nNv34ixe0wKBgFWdR0z0cqHjvzjrzvRDn6TSkdkzntm17BDGh5k6Crl3FMU0IZn0 +28EsQ0G2UUJgF+MVAq3RrPC627spRoaD5FqqF5KZRxxWwAWMQdOBD1dOQ58erf2H +aezmiGoIF9UBSE2y1HXiIQrcGhVjKtHNw3DrI0TWQ+K/N2xQUiXELmdvAoGANRSN +PuxBf56hOJnxg66aj+3cWCWWfidwd4IZyPzz78xBsWB464Up0FGm9cbHaaV7SkAD +TZ23Pcb/F6DoinIMJJD/9yOJoW3fLIY16WI3arOedjlGW6Ejkv7zcEL7mIhNjxM8 +EfjDNQ8hF0WItETDcMuKB7I0b5I5x1XDWYPno2ECgYEAsWHewaSG8+Ij8b+L/m0Z +lUD91L/gNVc6gdbjf5kMdYTCqI3q9N/9VWJyb7yRx8tjUTl9J//h7uYhCrujmpWf +1jcdaxqNLUUV7OcmM+PglprUe96A1zJwDOxc5DvHLbf/zBS6mA14PWYV1IUJdDdR +52wm5UEewSU9zlbvirgXj4U= +-----END PRIVATE KEY-----` + privRSAKeyPem = `-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAt3ZJUaztCRiVg87P0y8T7QMNFQi61BCSIKepxXXWc7zi5JJS MfQAstXJEqBYiShsSpYm6soiT6hX074t7wQAHGS3+u7qNogWpmTAUTUnNIM+QCxH @@ -445,6 +505,12 @@ wvvgOeCBovN3tSuGKzTiUKAAMAoGCCqGSM49BAMCAz8AMDwCHFap/5XDuqtXCG1g ljbYH5OWGBqGYCfL2k2+/6cCHAuk1bmOkGx7JAq/fSPd09i0DQIqUu7WHQHms48= -----END CERTIFICATE REQUEST-----` + privEC8KeyPem = `-----BEGIN PRIVATE KEY----- +MHgCAQAwEAYHKoZIzj0CAQYFK4EEACEEYTBfAgEBBBzN57mC5a72sATfYRlXLvZq +WghK+yzHuOGu6EDsoTwDOgAEwWd1V8ARPaHkkRKTOW9uT1ulori2+BF1qSmco4+e +dkGCmHu0AbhnvcL74DnggaLzd7Urhis04lA= +-----END PRIVATE KEY-----` + certECPem = `-----BEGIN CERTIFICATE----- MIIDJDCCAg6gAwIBAgIUM3J02tw0ZvpHUVHv6t8kcoft2/MwCwYJKoZIhvcNAQEL MBsxGTAXBgNVBAMMEFZhdWx0IFRlc3RpbmcgQ0EwHhcNMTUwNjE5MTcyODQyWhcN diff --git a/helper/certutil/types.go b/helper/certutil/types.go index 80209b5266..d25a7de85d 100644 --- a/helper/certutil/types.go +++ b/helper/certutil/types.go @@ -10,6 +10,8 @@ package certutil import ( "crypto" + "crypto/ecdsa" + "crypto/rsa" "crypto/tls" "crypto/x509" "encoding/pem" @@ -84,6 +86,7 @@ type CertBundle struct { // and a DER-encoded certificate type ParsedCertBundle struct { PrivateKeyType int + PKCS8 bool PrivateKeyBytes []byte PrivateKey crypto.Signer IssuingCABytes []byte @@ -137,6 +140,17 @@ func (c *CertBundle) ToParsedCertBundle() (*ParsedCertBundle, error) { } else if _, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes); err == nil { result.PrivateKeyType = RSAPrivateKey c.PrivateKeyType = "rsa" + } else if k, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes); err == nil { + result.PKCS8 = true + + switch k.(type) { + case *ecdsa.PrivateKey: + result.PrivateKeyType = ECPrivateKey + c.PrivateKeyType = "ec" + case *rsa.PrivateKey: + result.PrivateKeyType = RSAPrivateKey + c.PrivateKeyType = "rsa" + } } else { return nil, UserError{fmt.Sprintf("Unknown private key type in bundle: %s", c.PrivateKeyType)} } @@ -213,6 +227,9 @@ func (p *ParsedCertBundle) ToCertBundle() (*CertBundle, error) { default: return nil, InternalError{"Could not determine private key type when creating block"} } + if p.PKCS8 { + block.Type = "PRIVATE KEY" + } result.PrivateKey = strings.TrimSpace(string(pem.EncodeToMemory(&block))) } @@ -231,6 +248,21 @@ func (p *ParsedCertBundle) getSigner() (crypto.Signer, error) { return nil, UserError{"Given parsed cert bundle does not have private key information"} } + if p.PKCS8 { + if k, err := x509.ParsePKCS8PrivateKey(p.PrivateKeyBytes); err == nil { + switch k := k.(type) { + case *rsa.PrivateKey: + return k, nil + case *ecdsa.PrivateKey: + return k, nil + default: + return nil, UserError{fmt.Sprintf("Unable to determine pkcs8 key type")} + } + } else { + return nil, UserError{fmt.Sprintf("Error decoding pkcs8: %v", err)} + } + } + switch p.PrivateKeyType { case ECPrivateKey: signer, err = x509.ParseECPrivateKey(p.PrivateKeyBytes) From c4819554017555b4731872b353f871793da77b2a Mon Sep 17 00:00:00 2001 From: Andrew Stuart Date: Wed, 9 Dec 2015 13:52:55 -0700 Subject: [PATCH 13/19] Add pkcs8 flag setting in ParsePEMBundle --- helper/certutil/helpers.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/helper/certutil/helpers.go b/helper/certutil/helpers.go index 2facd2a387..1553a46aad 100644 --- a/helper/certutil/helpers.go +++ b/helper/certutil/helpers.go @@ -135,6 +135,8 @@ func ParsePEMBundle(pemBundle string) (*ParsedCertBundle, error) { parsedBundle.PrivateKeyBytes = pemBlock.Bytes parsedBundle.PrivateKey = signer } else if signer, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes); err == nil { + parsedBundle.PKCS8 = true + if parsedBundle.PrivateKeyType != UnknownPrivateKey { return nil, UserError{"More than one private key given; provide only one private key in the bundle"} } From 7065500d16ae6de2ef259b9a9df02411ab81b61a Mon Sep 17 00:00:00 2001 From: Andrew Stuart Date: Wed, 9 Dec 2015 14:55:03 -0700 Subject: [PATCH 14/19] Remove flag check before trying pkcs8 parsing. --- helper/certutil/types.go | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/helper/certutil/types.go b/helper/certutil/types.go index d25a7de85d..2b5c852a96 100644 --- a/helper/certutil/types.go +++ b/helper/certutil/types.go @@ -248,18 +248,12 @@ func (p *ParsedCertBundle) getSigner() (crypto.Signer, error) { return nil, UserError{"Given parsed cert bundle does not have private key information"} } - if p.PKCS8 { - if k, err := x509.ParsePKCS8PrivateKey(p.PrivateKeyBytes); err == nil { - switch k := k.(type) { - case *rsa.PrivateKey: - return k, nil - case *ecdsa.PrivateKey: - return k, nil - default: - return nil, UserError{fmt.Sprintf("Unable to determine pkcs8 key type")} - } - } else { - return nil, UserError{fmt.Sprintf("Error decoding pkcs8: %v", err)} + if k, err := x509.ParsePKCS8PrivateKey(p.PrivateKeyBytes); err == nil { + switch k := k.(type) { + case *rsa.PrivateKey: + return k, nil + case *ecdsa.PrivateKey: + return k, nil } } From 166c7ac0f9946fa796f0bad22f46524d372cfce0 Mon Sep 17 00:00:00 2001 From: Andrew Stuart Date: Thu, 10 Dec 2015 16:33:42 -0700 Subject: [PATCH 15/19] Remove debugging print statement in compareCertBundleToParsedCertBundle --- helper/certutil/certutil_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/helper/certutil/certutil_test.go b/helper/certutil/certutil_test.go index 2d03a42c12..0673bfc26b 100644 --- a/helper/certutil/certutil_test.go +++ b/helper/certutil/certutil_test.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/json" "fmt" - "log" "strings" "testing" @@ -158,7 +157,6 @@ func compareCertBundleToParsedCertBundle(cbut *CertBundle, pcbut *ParsedCertBund return fmt.Errorf("Bundle has wrong private key type") } if cb.PrivateKey != privRSAKeyPem && cb.PrivateKey != privRSA8KeyPem { - log.Println(cb.PrivateKey, privRSAKeyPem, privRSA8KeyPem) return fmt.Errorf("Bundle private key does not match") } case "ec": From 1c4172676694986dec27747b333d9a9da5fbc202 Mon Sep 17 00:00:00 2001 From: Andrew Stuart Date: Fri, 11 Dec 2015 09:58:49 -0700 Subject: [PATCH 16/19] Add benchmark for certutil bundle parsing --- helper/certutil/certutil_test.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/helper/certutil/certutil_test.go b/helper/certutil/certutil_test.go index 0673bfc26b..dc0721b146 100644 --- a/helper/certutil/certutil_test.go +++ b/helper/certutil/certutil_test.go @@ -49,6 +49,31 @@ func TestCertBundleConversion(t *testing.T) { } } +func BenchmarkCertBundleParsing(b *testing.B) { + for i := 0; i < b.N; i++ { + cbuts := []*CertBundle{ + refreshRSACertBundle(), + refreshRSA8CertBundle(), + refreshECCertBundle(), + refreshEC8CertBundle(), + } + + for i, cbut := range cbuts { + pcbut, err := cbut.ToParsedCertBundle() + if err != nil { + b.Logf("Error occurred with bundle %d in test array (index %d).\n", i+1, i) + b.Errorf("Error converting to parsed cert bundle: %s", err) + continue + } + + cbut, err = pcbut.ToCertBundle() + if err != nil { + b.Fatalf("Error converting to cert bundle: %s", err) + } + } + } +} + func TestCertBundleParsing(t *testing.T) { jsonBundle := refreshRSACertBundle() jsonString, err := json.Marshal(jsonBundle) From 44413fdb2f88ed751ec382c15274f962f06315a1 Mon Sep 17 00:00:00 2001 From: Andrew Stuart Date: Fri, 11 Dec 2015 15:47:00 -0700 Subject: [PATCH 17/19] Remove printf call from test --- helper/certutil/certutil_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/helper/certutil/certutil_test.go b/helper/certutil/certutil_test.go index e12b0b7159..1a90213b46 100644 --- a/helper/certutil/certutil_test.go +++ b/helper/certutil/certutil_test.go @@ -281,7 +281,6 @@ func compareCSRBundleToParsedCSRBundle(csrbut *CSRBundle, pcsrbut *ParsedCSRBund return fmt.Errorf("Bundle private key does not match") } default: - fmt.Printf("csrb = %+v\n", csrb) return fmt.Errorf("Bundle has unknown private key type") } From a73be107e11b3f458458bbd72f59a947ae2c9b48 Mon Sep 17 00:00:00 2001 From: Andrew Stuart Date: Mon, 14 Dec 2015 06:17:20 -0700 Subject: [PATCH 18/19] Remove unnecessary cast --- helper/certutil/types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helper/certutil/types.go b/helper/certutil/types.go index db686abf3f..882d4f09e2 100644 --- a/helper/certutil/types.go +++ b/helper/certutil/types.go @@ -283,7 +283,7 @@ func (p *ParsedCertBundle) getSigner() (crypto.Signer, error) { // SetParsedPrivateKey sets the private key parameters on the bundle func (p *ParsedCertBundle) SetParsedPrivateKey(privateKey crypto.Signer, privateKeyType PrivateKeyType, privateKeyBytes []byte) { p.PrivateKey = privateKey - p.PrivateKeyType = PrivateKeyType(privateKeyType) + p.PrivateKeyType = privateKeyType p.PrivateKeyBytes = privateKeyBytes } From fea21d9c081146b82f803bfa04fda7f43314a51d Mon Sep 17 00:00:00 2001 From: Andrew Stuart Date: Mon, 14 Dec 2015 11:16:47 -0700 Subject: [PATCH 19/19] Update PrivateKeyType to string, update switch statement. --- helper/certutil/types.go | 43 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/helper/certutil/types.go b/helper/certutil/types.go index 882d4f09e2..92ae652dd3 100644 --- a/helper/certutil/types.go +++ b/helper/certutil/types.go @@ -23,15 +23,16 @@ type Secret struct { Data map[string]interface{} `json:"data"` } -// The type of of the Private Key referenced in CertBundle -// and ParsedCertBundle. This uses colloquial names rather than -// official names, to eliminate confusion -type PrivateKeyType int +// PrivateKeyType holds a string representation of the type of private key (ec +// or rsa) referenced in CertBundle and ParsedCertBundle. This uses colloquial +// names rather than official names, to eliminate confusion +type PrivateKeyType string +//Well-known PrivateKeyTypes const ( - UnknownPrivateKey = PrivateKeyType(iota) - RSAPrivateKey - ECPrivateKey + UnknownPrivateKey PrivateKeyType = "" + RSAPrivateKey PrivateKeyType = "rsa" + ECPrivateKey PrivateKeyType = "ec" ) // TLSUsage controls whether the intended usage of a *tls.Config @@ -39,6 +40,7 @@ const ( // client use, or both, which affects which values are set type TLSUsage int +//Well-known TLSUsage types const ( TLSUnknown TLSUsage = 0 TLSServer TLSUsage = 1 << iota @@ -83,11 +85,11 @@ type ParsedPrivateKeyContainer interface { // a PEM-encoded certificate, and a string-encoded serial number, // returned from a successful Issue request type CertBundle struct { - PrivateKeyType string `json:"private_key_type" structs:"private_key_type" mapstructure:"private_key_type"` - Certificate string `json:"certificate" structs:"certificate" mapstructure:"certificate"` - IssuingCA string `json:"issuing_ca" structs:"issuing_ca" mapstructure:"issuing_ca"` - PrivateKey string `json:"private_key" structs:"private_key" mapstructure:"private_key"` - SerialNumber string `json:"serial_number" structs:"serial_number" mapstructure:"serial_number"` + PrivateKeyType PrivateKeyType `json:"private_key_type" structs:"private_key_type" mapstructure:"private_key_type"` + Certificate string `json:"certificate" structs:"certificate" mapstructure:"certificate"` + IssuingCA string `json:"issuing_ca" structs:"issuing_ca" mapstructure:"issuing_ca"` + PrivateKey string `json:"private_key" structs:"private_key" mapstructure:"private_key"` + SerialNumber string `json:"serial_number" structs:"serial_number" mapstructure:"serial_number"` } // ParsedCertBundle contains a key type, a DER-encoded private key, @@ -106,9 +108,9 @@ type ParsedCertBundle struct { // CSRBundle contains a key type, a PEM-encoded private key, // and a PEM-encoded CSR type CSRBundle struct { - PrivateKeyType string `json:"private_key_type" structs:"private_key_type" mapstructure:"private_key_type"` - CSR string `json:"csr" structs:"csr" mapstructure:"csr"` - PrivateKey string `json:"private_key" structs:"private_key" mapstructure:"private_key"` + PrivateKeyType PrivateKeyType `json:"private_key_type" structs:"private_key_type" mapstructure:"private_key_type"` + CSR string `json:"csr" structs:"csr" mapstructure:"csr"` + PrivateKey string `json:"private_key" structs:"private_key" mapstructure:"private_key"` } // ParsedCSRBundle contains a key type, a DER-encoded private key, @@ -139,11 +141,9 @@ func (c *CertBundle) ToParsedCertBundle() (*ParsedCertBundle, error) { switch result.PrivateKeyFormat { case ECBlock: - c.PrivateKeyType = "ec" - result.PrivateKeyType = ECPrivateKey + result.PrivateKeyType, c.PrivateKeyType = ECPrivateKey, ECPrivateKey case PKCS1Block: - c.PrivateKeyType = "rsa" - result.PrivateKeyType = RSAPrivateKey + c.PrivateKeyType, result.PrivateKeyType = RSAPrivateKey, RSAPrivateKey case PKCS8Block: t, err := getPKCS8Type(pemBlock.Bytes) if err != nil { @@ -152,9 +152,9 @@ func (c *CertBundle) ToParsedCertBundle() (*ParsedCertBundle, error) { result.PrivateKeyType = t switch t { case ECPrivateKey: - c.PrivateKeyType = "ec" + c.PrivateKeyType = ECPrivateKey case RSAPrivateKey: - c.PrivateKeyType = "rsa" + c.PrivateKeyType = RSAPrivateKey } default: return nil, UserError{fmt.Sprintf("Unsupported key block type: %s", pemBlock.Type)} @@ -222,6 +222,7 @@ func (p *ParsedCertBundle) ToCertBundle() (*CertBundle, error) { if p.PrivateKeyBytes != nil && len(p.PrivateKeyBytes) > 0 { block.Type = string(p.PrivateKeyFormat) block.Bytes = p.PrivateKeyBytes + result.PrivateKeyType = p.PrivateKeyType //Handle bundle not parsed by us if block.Type == "" {