Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 41 additions & 19 deletions pkg/asset/tls/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,31 +63,53 @@ func PublicKeyToPem(key *rsa.PublicKey) ([]byte, error) {
}

// PemToPrivateKey converts a PEM data block to a private key (RSA or ECDSA).
// It iterates through all PEM blocks to tolerate non-key blocks such as
// EC PARAMETERS that OpenSSL may prepend to EC private keys.
func PemToPrivateKey(data []byte) (crypto.PrivateKey, error) {
block, _ := pem.Decode(data)
if block == nil {
return nil, errors.Errorf("could not find a PEM block in the private key")
}

switch block.Type {
case "RSA PRIVATE KEY":
return x509.ParsePKCS1PrivateKey(block.Bytes)
case "EC PRIVATE KEY":
return x509.ParseECPrivateKey(block.Bytes)
case "PRIVATE KEY":
key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, err
rest := data
var parseErr error
for {
var block *pem.Block
block, rest = pem.Decode(rest)
if block == nil {
break
}
switch key.(type) {
case *rsa.PrivateKey, *ecdsa.PrivateKey:

switch block.Type {
case "RSA PRIVATE KEY":
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
parseErr = err
continue
}
return key, nil
case "EC PRIVATE KEY":
key, err := x509.ParseECPrivateKey(block.Bytes)
if err != nil {
parseErr = err
continue
}
return key, nil
case "PRIVATE KEY":
key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
parseErr = err
continue
}
switch key.(type) {
case *rsa.PrivateKey, *ecdsa.PrivateKey:
return key, nil
default:
return nil, fmt.Errorf("unsupported PKCS#8 key type: %T", key)
}
default:
return nil, fmt.Errorf("unsupported PKCS#8 key type: %T", key)
continue
}
default:
return nil, fmt.Errorf("unsupported PEM block type: %s", block.Type)
}
if parseErr != nil {
return nil, parseErr
}
return nil, errors.Errorf("could not find a PEM block with a supported private key type")
}

// PemToPublicKey converts a data block to rsa.PublicKey.
Expand Down
53 changes: 53 additions & 0 deletions pkg/asset/tls/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,57 @@ func TestPemToPrivateKeyFormats(t *testing.T) {
_, ok := decoded.(*rsa.PrivateKey)
assert.True(t, ok, "expected *rsa.PrivateKey")
})

t.Run("EC key with EC PARAMETERS prefix", func(t *testing.T) {
ecKey, err := GenerateECDSAPrivateKey(types.ECDSACurveP256)
assert.NoError(t, err)
ecDER, err := x509.MarshalECPrivateKey(ecKey)
assert.NoError(t, err)

paramBlock := pem.EncodeToMemory(&pem.Block{
Type: "EC PARAMETERS",
Bytes: []byte{0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07},
})
keyBlock := pem.EncodeToMemory(&pem.Block{
Type: "EC PRIVATE KEY",
Bytes: ecDER,
})
combined := make([]byte, 0, len(paramBlock)+len(keyBlock))
combined = append(combined, paramBlock...)
combined = append(combined, keyBlock...)

decoded, err := PemToPrivateKey(combined)
assert.NoError(t, err)
_, ok := decoded.(*ecdsa.PrivateKey)
assert.True(t, ok, "expected *ecdsa.PrivateKey")
})

t.Run("unsupported block type only", func(t *testing.T) {
pemBytes := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: []byte("not a key"),
})
_, err := PemToPrivateKey(pemBytes)
assert.Error(t, err)
assert.Contains(t, err.Error(), "could not find a PEM block with a supported private key type")
})

t.Run("malformed key block followed by valid key", func(t *testing.T) {
badBlock := pem.EncodeToMemory(&pem.Block{
Type: "EC PRIVATE KEY",
Bytes: []byte("malformed key data"),
})
key, err := GenerateECDSAPrivateKey(types.ECDSACurveP256)
assert.NoError(t, err)
goodPem, err := PrivateKeyToPem(key)
assert.NoError(t, err)
combined := make([]byte, 0, len(badBlock)+len(goodPem))
combined = append(combined, badBlock...)
combined = append(combined, goodPem...)

decoded, err := PemToPrivateKey(combined)
assert.NoError(t, err)
_, ok := decoded.(*ecdsa.PrivateKey)
assert.True(t, ok, "expected *ecdsa.PrivateKey")
})
}