Skip to content

Commit

Permalink
Add support for importing PKCS#8 private keys, and add validation (#1300
Browse files Browse the repository at this point in the history
)

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]>
  • Loading branch information
haydentherapper authored Jan 12, 2022
1 parent aa0b8c1 commit 754d33e
Show file tree
Hide file tree
Showing 2 changed files with 288 additions and 22 deletions.
86 changes: 76 additions & 10 deletions pkg/cosign/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package cosign
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
Expand All @@ -37,10 +38,14 @@ import (
)

const (
PrivateKeyPemType = "ENCRYPTED COSIGN PRIVATE KEY"
CosignPrivateKeyPemType = "ENCRYPTED COSIGN PRIVATE KEY"
// PEM-encoded PKCS #1 RSA private key
RSAPrivateKeyPemType = "RSA PRIVATE KEY"
ECPrivateKeyPemType = "EC PRIVATE KEY"
BundleKey = static.BundleAnnotationKey
// PEM-encoded ECDSA private key
ECPrivateKeyPemType = "EC PRIVATE KEY"
// PEM-encoded PKCS #8 RSA, ECDSA or ED25519 private key
PrivateKeyPemType = "PRIVATE KEY"
BundleKey = static.BundleAnnotationKey
)

type PassFunc func(bool) ([]byte, error)
Expand All @@ -56,6 +61,35 @@ type KeysBytes struct {
password []byte
}

// Enforce a minimum and maximum RSA key size.
func validateRsaKey(pk *rsa.PrivateKey) error {
// Key size is the bit length of modulus
keySize := pk.N.BitLen()
if keySize < 2048 {
return fmt.Errorf("rsa key size too small, expected >= 2048")
}
if keySize > 4096 {
return fmt.Errorf("rsa key size too large, expected <= 4096")
}
return nil
}

// Enforce that the ECDSA key curve is one of:
// * NIST P-256 (secp256r1, prime256v1)
// * NIST P-384
// * NIST P-521.
// Other EC curves, like secp256k1, are not supported by Go.
func validateEcdsaKey(pk *ecdsa.PrivateKey) error {
switch pk.Curve {
case elliptic.P224():
return fmt.Errorf("unsupported ec curve, expected NIST P-256, P-384, or P-521")
case elliptic.P256(), elliptic.P384(), elliptic.P521():
return nil
default:
return fmt.Errorf("unexpected ec curve")
}
}

func GeneratePrivateKey() (*ecdsa.PrivateKey, error) {
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
}
Expand All @@ -75,15 +109,47 @@ func ImportKeyPair(keyPath string, pf PassFunc) (*KeysBytes, error) {

switch p.Type {
case RSAPrivateKeyPemType:
pk, err = x509.ParsePKCS1PrivateKey(p.Bytes)
rsaPk, err := x509.ParsePKCS1PrivateKey(p.Bytes)
if err != nil {
return nil, fmt.Errorf("parsing error")
return nil, fmt.Errorf("error parsing rsa private key")
}
default:
pk, err = x509.ParseECPrivateKey(p.Bytes)
if err = validateRsaKey(rsaPk); err != nil {
return nil, errors.Wrap(err, "error validating rsa key")
}
pk = rsaPk
case ECPrivateKeyPemType:
ecdsaPk, err := x509.ParseECPrivateKey(p.Bytes)
if err != nil {
return nil, fmt.Errorf("parsing error")
return nil, fmt.Errorf("error parsing ecdsa private key")
}
if err = validateEcdsaKey(ecdsaPk); err != nil {
return nil, errors.Wrap(err, "error validating ecdsa key")
}
pk = ecdsaPk
case PrivateKeyPemType:
pkcs8Pk, err := x509.ParsePKCS8PrivateKey(p.Bytes)
if err != nil {
return nil, fmt.Errorf("error parsing pkcs #8 private key")
}
switch k := pkcs8Pk.(type) {
case *rsa.PrivateKey:
if err = validateRsaKey(k); err != nil {
return nil, errors.Wrap(err, "error validating rsa key")
}
pk = k
case *ecdsa.PrivateKey:
if err = validateEcdsaKey(k); err != nil {
return nil, errors.Wrap(err, "error validating ecdsa key")
}
pk = k
case ed25519.PrivateKey:
// Nothing to validate, since ED25519 supports only one key size.
pk = k
default:
return nil, fmt.Errorf("unexpected private key")
}
default:
return nil, fmt.Errorf("unsupported private key")
}
return marshalKeyPair(Keys{pk, pk.Public()}, pf)
}
Expand All @@ -107,7 +173,7 @@ func marshalKeyPair(keypair Keys, pf PassFunc) (*KeysBytes, error) {
// store in PEM format
privBytes := pem.EncodeToMemory(&pem.Block{
Bytes: encBytes,
Type: PrivateKeyPemType,
Type: CosignPrivateKeyPemType,
})

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

Expand Down
Loading

0 comments on commit 754d33e

Please sign in to comment.