Skip to content

Commit 754d33e

Browse files
Add support for importing PKCS#8 private keys, and add validation (#1300)
This adds support for PKCS#8 encoded private keys, which means we can also import ED25519 keys. I've added tests for PKCS#8 RSA and ECDSA keys too. I also added some validation of keys before importing. For RSA, we will require that the key size be between 2048 and 4096. For ECDSA keys, we will only disallow NIST P-224, since Cosign generates P-256 by default. Other curves are not supported by Go's crypto library. Signed-off-by: Hayden Blauzvern <[email protected]>
1 parent aa0b8c1 commit 754d33e

File tree

2 files changed

+288
-22
lines changed

2 files changed

+288
-22
lines changed

pkg/cosign/keys.go

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package cosign
1818
import (
1919
"crypto"
2020
"crypto/ecdsa"
21+
"crypto/ed25519"
2122
"crypto/elliptic"
2223
"crypto/rand"
2324
"crypto/rsa"
@@ -37,10 +38,14 @@ import (
3738
)
3839

3940
const (
40-
PrivateKeyPemType = "ENCRYPTED COSIGN PRIVATE KEY"
41+
CosignPrivateKeyPemType = "ENCRYPTED COSIGN PRIVATE KEY"
42+
// PEM-encoded PKCS #1 RSA private key
4143
RSAPrivateKeyPemType = "RSA PRIVATE KEY"
42-
ECPrivateKeyPemType = "EC PRIVATE KEY"
43-
BundleKey = static.BundleAnnotationKey
44+
// PEM-encoded ECDSA private key
45+
ECPrivateKeyPemType = "EC PRIVATE KEY"
46+
// PEM-encoded PKCS #8 RSA, ECDSA or ED25519 private key
47+
PrivateKeyPemType = "PRIVATE KEY"
48+
BundleKey = static.BundleAnnotationKey
4449
)
4550

4651
type PassFunc func(bool) ([]byte, error)
@@ -56,6 +61,35 @@ type KeysBytes struct {
5661
password []byte
5762
}
5863

64+
// Enforce a minimum and maximum RSA key size.
65+
func validateRsaKey(pk *rsa.PrivateKey) error {
66+
// Key size is the bit length of modulus
67+
keySize := pk.N.BitLen()
68+
if keySize < 2048 {
69+
return fmt.Errorf("rsa key size too small, expected >= 2048")
70+
}
71+
if keySize > 4096 {
72+
return fmt.Errorf("rsa key size too large, expected <= 4096")
73+
}
74+
return nil
75+
}
76+
77+
// Enforce that the ECDSA key curve is one of:
78+
// * NIST P-256 (secp256r1, prime256v1)
79+
// * NIST P-384
80+
// * NIST P-521.
81+
// Other EC curves, like secp256k1, are not supported by Go.
82+
func validateEcdsaKey(pk *ecdsa.PrivateKey) error {
83+
switch pk.Curve {
84+
case elliptic.P224():
85+
return fmt.Errorf("unsupported ec curve, expected NIST P-256, P-384, or P-521")
86+
case elliptic.P256(), elliptic.P384(), elliptic.P521():
87+
return nil
88+
default:
89+
return fmt.Errorf("unexpected ec curve")
90+
}
91+
}
92+
5993
func GeneratePrivateKey() (*ecdsa.PrivateKey, error) {
6094
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
6195
}
@@ -75,15 +109,47 @@ func ImportKeyPair(keyPath string, pf PassFunc) (*KeysBytes, error) {
75109

76110
switch p.Type {
77111
case RSAPrivateKeyPemType:
78-
pk, err = x509.ParsePKCS1PrivateKey(p.Bytes)
112+
rsaPk, err := x509.ParsePKCS1PrivateKey(p.Bytes)
79113
if err != nil {
80-
return nil, fmt.Errorf("parsing error")
114+
return nil, fmt.Errorf("error parsing rsa private key")
81115
}
82-
default:
83-
pk, err = x509.ParseECPrivateKey(p.Bytes)
116+
if err = validateRsaKey(rsaPk); err != nil {
117+
return nil, errors.Wrap(err, "error validating rsa key")
118+
}
119+
pk = rsaPk
120+
case ECPrivateKeyPemType:
121+
ecdsaPk, err := x509.ParseECPrivateKey(p.Bytes)
84122
if err != nil {
85-
return nil, fmt.Errorf("parsing error")
123+
return nil, fmt.Errorf("error parsing ecdsa private key")
86124
}
125+
if err = validateEcdsaKey(ecdsaPk); err != nil {
126+
return nil, errors.Wrap(err, "error validating ecdsa key")
127+
}
128+
pk = ecdsaPk
129+
case PrivateKeyPemType:
130+
pkcs8Pk, err := x509.ParsePKCS8PrivateKey(p.Bytes)
131+
if err != nil {
132+
return nil, fmt.Errorf("error parsing pkcs #8 private key")
133+
}
134+
switch k := pkcs8Pk.(type) {
135+
case *rsa.PrivateKey:
136+
if err = validateRsaKey(k); err != nil {
137+
return nil, errors.Wrap(err, "error validating rsa key")
138+
}
139+
pk = k
140+
case *ecdsa.PrivateKey:
141+
if err = validateEcdsaKey(k); err != nil {
142+
return nil, errors.Wrap(err, "error validating ecdsa key")
143+
}
144+
pk = k
145+
case ed25519.PrivateKey:
146+
// Nothing to validate, since ED25519 supports only one key size.
147+
pk = k
148+
default:
149+
return nil, fmt.Errorf("unexpected private key")
150+
}
151+
default:
152+
return nil, fmt.Errorf("unsupported private key")
87153
}
88154
return marshalKeyPair(Keys{pk, pk.Public()}, pf)
89155
}
@@ -107,7 +173,7 @@ func marshalKeyPair(keypair Keys, pf PassFunc) (*KeysBytes, error) {
107173
// store in PEM format
108174
privBytes := pem.EncodeToMemory(&pem.Block{
109175
Bytes: encBytes,
110-
Type: PrivateKeyPemType,
176+
Type: CosignPrivateKeyPemType,
111177
})
112178

113179
// Now do the public key
@@ -154,7 +220,7 @@ func LoadPrivateKey(key []byte, pass []byte) (signature.SignerVerifier, error) {
154220
if p == nil {
155221
return nil, errors.New("invalid pem block")
156222
}
157-
if p.Type != PrivateKeyPemType {
223+
if p.Type != CosignPrivateKeyPemType {
158224
return nil, fmt.Errorf("unsupported pem type: %s", p.Type)
159225
}
160226

0 commit comments

Comments
 (0)