Skip to content

Commit

Permalink
support parsing different private key types
Browse files Browse the repository at this point in the history
Currently ImportKeyPair() in pkg/cosign supports
only private keys in PKCS #8 form. This change
extends it to also support PKCS #1 for RSA keys
("RSA PUBLIC KEY") and SEC 1 for EC keys
("EC PRIVATE KEY").

Fix #3775.

Signed-off-by: Dmitry S <[email protected]>
  • Loading branch information
dmitris committed Jul 10, 2024
1 parent ca682f2 commit e756968
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 11 deletions.
39 changes: 30 additions & 9 deletions pkg/cosign/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ type Keys struct {
public crypto.PublicKey
}

// TODO(jason): Move this to an internal package.
type KeysBytes struct {
PrivateBytes []byte
PublicBytes []byte
Expand All @@ -69,12 +68,16 @@ func (k *KeysBytes) Password() []byte {
return k.password
}

// TODO(jason): Move this to an internal package.
func GeneratePrivateKey() (*ecdsa.PrivateKey, error) {
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
}

// TODO(jason): Move this to the only place it's used in cmd/cosign/cli/importkeypair, and unexport it.
// ImportKeyPair imports a key pair from a file containing a PEM-encoded
// private key encoded with a password provided by the 'pf' function.
// The private key can be in one of the following formats:
// - RSA private key (PKCS #1)
// - ECDSA private key
// - PKCS #8 private key (RSA, ECDSA or ED25519).
func ImportKeyPair(keyPath string, pf PassFunc) (*KeysBytes, error) {
kb, err := os.ReadFile(filepath.Clean(keyPath))
if err != nil {
Expand Down Expand Up @@ -180,7 +183,6 @@ func marshalKeyPair(ptype string, keypair Keys, pf PassFunc) (key *KeysBytes, er
}, nil
}

// TODO(jason): Move this to an internal package.
func GenerateKeyPair(pf PassFunc) (*KeysBytes, error) {
priv, err := GeneratePrivateKey()
if err != nil {
Expand All @@ -191,7 +193,6 @@ func GenerateKeyPair(pf PassFunc) (*KeysBytes, error) {
return marshalKeyPair(SigstorePrivateKeyPemType, Keys{priv, priv.Public()}, pf)
}

// TODO(jason): Move this to an internal package.
func PemToECDSAKey(pemBytes []byte) (*ecdsa.PublicKey, error) {
pub, err := cryptoutils.UnmarshalPEMToPublicKey(pemBytes)
if err != nil {
Expand All @@ -204,7 +205,13 @@ func PemToECDSAKey(pemBytes []byte) (*ecdsa.PublicKey, error) {
return ecdsaPub, nil
}

// TODO(jason): Move this to pkg/signature, the only place it's used, and unimport it.
// LoadPrivateKey loads a cosign PEM private key encrypted with the given passphrase,
// and returns a SignerVerifier instance.
//
// Once decrypted, the private key can be in one of the following formats:
// - RSA private key (PKCS #1)
// - ECDSA private key
// - PKCS #8 private key (RSA, ECDSA or ED25519).
func LoadPrivateKey(key []byte, pass []byte) (signature.SignerVerifier, error) {
// Decrypt first
p, _ := pem.Decode(key)
Expand All @@ -219,10 +226,9 @@ func LoadPrivateKey(key []byte, pass []byte) (signature.SignerVerifier, error) {
if err != nil {
return nil, fmt.Errorf("decrypt: %w", err)
}

pk, err := x509.ParsePKCS8PrivateKey(x509Encoded)
pk, err := parsePrivateKey(x509Encoded)
if err != nil {
return nil, fmt.Errorf("parsing private key: %w", err)
return nil, err
}
switch pk := pk.(type) {
case *rsa.PrivateKey:
Expand All @@ -235,3 +241,18 @@ func LoadPrivateKey(key []byte, pass []byte) (signature.SignerVerifier, error) {
return nil, errors.New("unsupported key type")
}
}

// given already decrypted blob with x509 encoded private key, try different x509.Parse<*>PrivateKey
// functions to load it.
func parsePrivateKey(key []byte) (crypto.PrivateKey, error) {
if pk, err := x509.ParsePKCS8PrivateKey(key); err == nil {
return pk, nil
}
if pk, err := x509.ParseECPrivateKey(key); err == nil {
return pk, nil
}
if pk, err := x509.ParsePKCS1PrivateKey(key); err == nil {
return pk, nil
}
return nil, errors.New("parse private key: unknown type")
}
56 changes: 54 additions & 2 deletions pkg/cosign/keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,35 @@ pGAaLxggvtvuncMuTrG+cdmsR9SafSFKRS92NCxhOUonQ+NP6mLskIGzJZoQ5JvQ
qGzRVIDGbNkrVHM0IsAtHRpC0rYrtZY+9OwiraGcsqUMLwwQdCA=
-----END RSA PRIVATE KEY-----`

// RSA 2048 key encoded with PCKS#1
const validrsapkcs1 = `-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAqvVkqzBrMzl4TC5VsBgXnQiCo861QcB1TqdwgGzYkrNdF6fr
UisPWgjMJixolmpwHv+088rsbiSlD9hc9DmzgCJPto7wvGeTILa9cNHCGKm12q7K
TFnVUTH9z6D4E3F/IsI22Pg8+cSyeDn+LKxMadTohlcXJ8jqcH75KezoGngMp5OJ
vqs93lkLep+UJMspV029z7PzF9uoT8gI02Adfq5Zkfu8VmIy8gkpYgTBCpNnD01u
vo6HoAYG/mHqgPYivWBwi221GPjJWmCPB1rIJHlpAYUETA5jUY2UwP1oQx54ybcj
eNjMRP5J0cGyOI8MT0j9ul7/DJde3Ds5A7BA9QIDAQABAoIBAQCJ3Q5rhsZMLsI2
HP943FTei+heFOnStlNjNF/jEOOtmfsugnmgb50XrBSFjDZjZj44oVjZaQE06VQ6
7O44/PcmE4VY4Ph91sCtFvC6NE1j+ifuzBnTbHY73iah81tawqIV86yrV7REbzzE
+29fsyqEBe/ltgG0Ua/NPHfOOYALJwZVx8ozkz7xOyU23kNxSzp3T0FBnYYIuzrI
a4h7FVxGLbIJQ3xWBU5xkd4m7EqgFYkWCfSXAVoLT2z7eJSYAmITuiQXl8uDz1XY
lWKgOwkRJrMVVD8hDME7Hoc/RlKmYX64IZ3lv70NuyKDPTuhmoIQRJ49mVaqdPtH
v0Z9L0tBAoGBAMPFcJFaR+VdmsZ2DXQlsPQNAB064SYbIXx/pxNVoDYkHyMfZsf3
vjf4gMKNsHTM4u812UpsE5762OqdVKmWXQc60mkuEk7N55iXuBJiJxSpuj6IbiLw
ogV+B40UC9luOISQpDYdY1Km1ho4HRngNkXMlJ48tFuwIP3lwwz3FtFZAoGBAN+N
wVssBvNhHzGfcUMxxCwJKfHCx1ANWuTe+AsDtpZRTExMcX1PH1euxUV9aII9Klg7
A7FN1It78pDrQBNQJoeMON+5N53//geY6stDfhPkOoT8Zqg2VEz4WRihUgAUHESk
pUVYSvEXG7J7AG5iGgn0B3P9PMvvReIHnTeQ1rz9AoGAWAR31NHrSyMniBzhdZvQ
kBkcOQgU3AYMqyXVXyr7KfxZh3gBxNwMyKtQcKg1cn3/dZ8XP4+RzsNnLSxpOQni
b3Kx0RomnwmSG5fy6Uj52x9oHd9G7SyVG7UK/hHKNgqJHIjPW4kg87MQxZ7+7nhQ
zlbpZq9SQ3rPind3l2er+ZkCgYASFs9ZiEN7uBUlF8i7bjB4e7lYJbGpCZucP2qE
waUpnqR03A6m3BsmJi8yQ0aMm1Rs1UGkPC8BpmLnVRHXPjoP58nGWJ9meotcpAQD
tI9kHqiZkC7iV5sUq1fSRWN0PCxZZZU1+kH+JieIlqlfRTLkMUnVGd2shsz50DHp
iB/IJQKBgAO3kRRVszif2jdo7gzDsiSQ+fSyD6yEE9eP+uNwLZw9bQhyW5wlXF+t
dR5olNrc0bP542MHL5vigRnezq9hT1hbLkQg/MA2k5FrMHIZshfWITnI5B5I2sw6
wu/XEVtNr8RincoHXjov4DiqgbLPWubM7FHLN5CW6nRLXhGkb4+7
-----END RSA PRIVATE KEY-----`

// RSA 2048 key encoded with PKCS#8
const validrsapkcs8 = `-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCwDtRl4McMhk4Q
Expand Down Expand Up @@ -257,7 +286,7 @@ const ed25519key = `-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIALEbo1EFnWFqBK/wC+hhypG/8hXEerwdNetAoFoFVdv
-----END PRIVATE KEY-----`

// COSIGN labeled key
// COSIGN labeled RSA key
const pemcosignkey = `-----BEGIN ENCRYPTED COSIGN PRIVATE KEY-----
eyJrZGYiOnsibmFtZSI6InNjcnlwdCIsInBhcmFtcyI6eyJOIjozMjc2OCwiciI6
OCwicCI6MX0sInNhbHQiOiJ4WWdoc09JTUxUWGNOT0RsclNIOUNKc1FlOVFnZmN1
Expand All @@ -270,6 +299,19 @@ Y1pmbEJheXZMV3pXblo4d2NDZ2ZpT1o1VXlRTEFJMHh0dnR6dEh3cTdDV1Vhd3V4
RlhlNDZzck9TUE9SNHN6bytabWErUGovSFE9PSJ9
-----END ENCRYPTED COSIGN PRIVATE KEY-----`

// COSIGN labeled EC key
const pemcosigneckey = `-----BEGIN ENCRYPTED COSIGN PRIVATE KEY-----
eyJrZGYiOnsibmFtZSI6InNjcnlwdCIsInBhcmFtcyI6eyJOIjo2NTUzNiwiciI6
OCwicCI6MX0sInNhbHQiOiJHK3F5WTYrNzhNS0JzMXNGTGs1ajYwcS9kS3Z1czBW
VkhlSHZybC9POTF3PSJ9LCJjaXBoZXIiOnsibmFtZSI6Im5hY2wvc2VjcmV0Ym94
Iiwibm9uY2UiOiJRc2JGdG13WDRDK2ttV3ZCcVRaMEFGOUFYdk1jRmg1SCJ9LCJj
aXBoZXJ0ZXh0IjoiREM5T28zeldiYVQzSXYwdFVnWEdycjUxYW1samwwNlQ5MTNP
VkxPbWpuMWhnK2o2WXRUbWg3SGhZSlY1N2J5eGE0Q281bE9YYmRqbTJ3aklubEd1
Um5aZCt5OExnekpSNzFSeEhKVzgrWmRlcFJmYWJMTjdHbDgrSFZEcERVQ3NxQnRh
VngyblpGbFEwWUl1anZwbFphblNGaUVvdERLVGkxZ3VhUXIwUHNzYU01NXZxbTRY
WS9rPSJ9
-----END ENCRYPTED COSIGN PRIVATE KEY-----`

// SIGSTORE labeled key
const pemsigstorekey = `-----BEGIN ENCRYPTED SIGSTORE PRIVATE KEY-----
eyJrZGYiOnsibmFtZSI6InNjcnlwdCIsInBhcmFtcyI6eyJOIjozMjc2OCwiciI6
Expand Down Expand Up @@ -323,10 +365,15 @@ func TestReadingPrivatePemTypes(t *testing.T) {
expected error
}{
{
pemType: "COSIGN PEM Type",
pemType: "COSIGN PEM RSA Type",
pemData: []byte(pemcosignkey),
expected: nil,
},
{
pemType: "COSIGN PEM EC Type",
pemData: []byte(pemcosigneckey),
expected: nil,
},
{
pemType: "SISTORE PEM Type",
pemData: []byte(pemsigstorekey),
Expand Down Expand Up @@ -363,6 +410,11 @@ func TestImportPrivateKey(t *testing.T) {
pemData: validrsa,
expected: nil,
},
{
fileName: "validrsapkcs1.key",
pemData: validrsapkcs1,
expected: nil,
},
{
fileName: "validrsapkcs8.key",
pemData: validrsapkcs8,
Expand Down

0 comments on commit e756968

Please sign in to comment.