diff --git a/client/backwards_compatibility_test.go b/client/backwards_compatibility_test.go index c825200cf..edbc247bf 100644 --- a/client/backwards_compatibility_test.go +++ b/client/backwards_compatibility_test.go @@ -176,6 +176,9 @@ func Test0Dot3Migration(t *testing.T) { // We can read and publish from notary0.1 repos func Test0Dot1RepoFormat(t *testing.T) { + if notary.FIPSEnabled() { + t.Skip("skip backward compatibility test in FIPS mode") + } // make a temporary directory and copy the fixture into it, since updating // and publishing will modify the files tmpDir, err := ioutil.TempDir("", "notary-backwards-compat-test") @@ -236,6 +239,9 @@ func Test0Dot1RepoFormat(t *testing.T) { // We can read and publish from notary0.3 repos func Test0Dot3RepoFormat(t *testing.T) { + if notary.FIPSEnabled() { + t.Skip("skip backward compatibility test in FIPS mode") + } // make a temporary directory and copy the fixture into it, since updating // and publishing will modify the files tmpDir, err := ioutil.TempDir("", "notary-backwards-compat-test") diff --git a/cmd/notary/integration_test.go b/cmd/notary/integration_test.go index 21ad0d317..1b7be7fa3 100644 --- a/cmd/notary/integration_test.go +++ b/cmd/notary/integration_test.go @@ -111,7 +111,7 @@ func TestInitWithRootKey(t *testing.T) { require.NoError(t, err) // if the key has a root role, AddKey sets the gun to "" so we have done the same here - encryptedPEMPrivKey, err := utils.EncryptPrivateKey(privKey, data.CanonicalRootRole, "", testPassphrase) + encryptedPEMPrivKey, err := utils.ConvertPrivateKeyToPKCS8(privKey, data.CanonicalRootRole, "", testPassphrase) require.NoError(t, err) encryptedPEMKeyFilename := filepath.Join(tempDir, "encrypted_key.key") err = ioutil.WriteFile(encryptedPEMKeyFilename, encryptedPEMPrivKey, 0644) @@ -146,7 +146,7 @@ func TestInitWithRootKey(t *testing.T) { // check error if unencrypted PEM used unencryptedPrivKey, err := utils.GenerateECDSAKey(rand.Reader) require.NoError(t, err) - unencryptedPEMPrivKey, err := utils.KeyToPEM(unencryptedPrivKey, data.CanonicalRootRole, "") + unencryptedPEMPrivKey, err := utils.ConvertPrivateKeyToPKCS8(unencryptedPrivKey, data.CanonicalRootRole, "", "") require.NoError(t, err) unencryptedPEMKeyFilename := filepath.Join(tempDir, "unencrypted_key.key") err = ioutil.WriteFile(unencryptedPEMKeyFilename, unencryptedPEMPrivKey, 0644) @@ -161,7 +161,7 @@ func TestInitWithRootKey(t *testing.T) { require.NoError(t, err) // Blank gun name since it is a root key - badPassPEMPrivKey, err := utils.EncryptPrivateKey(badPassPrivKey, data.CanonicalRootRole, "", "bad_pass") + badPassPEMPrivKey, err := utils.ConvertPrivateKeyToPKCS8(badPassPrivKey, data.CanonicalRootRole, "", "bad_pass") require.NoError(t, err) badPassPEMKeyFilename := filepath.Join(tempDir, "badpass_key.key") err = ioutil.WriteFile(badPassPEMKeyFilename, badPassPEMPrivKey, 0644) @@ -173,7 +173,7 @@ func TestInitWithRootKey(t *testing.T) { // check error if wrong role specified snapshotPrivKey, err := utils.GenerateECDSAKey(rand.Reader) require.NoError(t, err) - snapshotPEMPrivKey, err := utils.KeyToPEM(snapshotPrivKey, data.CanonicalSnapshotRole, "gun2") + snapshotPEMPrivKey, err := utils.ConvertPrivateKeyToPKCS8(snapshotPrivKey, data.CanonicalSnapshotRole, "gun2", "") require.NoError(t, err) snapshotPEMKeyFilename := filepath.Join(tempDir, "snapshot_key.key") err = ioutil.WriteFile(snapshotPEMKeyFilename, snapshotPEMPrivKey, 0644) @@ -1150,9 +1150,9 @@ func TestClientDelegationsPublishing(t *testing.T) { tempFile.Close() defer os.Remove(tempFile.Name()) - privKeyBytesNoRole, err := utils.KeyToPEM(privKey, "", "") + privKeyBytesNoRole, err := utils.ConvertPrivateKeyToPKCS8(privKey, "", "", "") require.NoError(t, err) - privKeyBytesWithRole, err := utils.KeyToPEM(privKey, "user", "") + privKeyBytesWithRole, err := utils.ConvertPrivateKeyToPKCS8(privKey, "user", "", "") require.NoError(t, err) // Set up targets for publishing @@ -1582,7 +1582,7 @@ func TestKeyRotation(t *testing.T) { // create encrypted root keys rootPrivKey1, err := utils.GenerateECDSAKey(rand.Reader) require.NoError(t, err) - encryptedPEMPrivKey1, err := utils.EncryptPrivateKey(rootPrivKey1, data.CanonicalRootRole, "", testPassphrase) + encryptedPEMPrivKey1, err := utils.ConvertPrivateKeyToPKCS8(rootPrivKey1, data.CanonicalRootRole, "", testPassphrase) require.NoError(t, err) encryptedPEMKeyFilename1 := filepath.Join(tempDir, "encrypted_key.key") err = ioutil.WriteFile(encryptedPEMKeyFilename1, encryptedPEMPrivKey1, 0644) @@ -1590,7 +1590,7 @@ func TestKeyRotation(t *testing.T) { rootPrivKey2, err := utils.GenerateECDSAKey(rand.Reader) require.NoError(t, err) - encryptedPEMPrivKey2, err := utils.EncryptPrivateKey(rootPrivKey2, data.CanonicalRootRole, "", testPassphrase) + encryptedPEMPrivKey2, err := utils.ConvertPrivateKeyToPKCS8(rootPrivKey2, data.CanonicalRootRole, "", testPassphrase) require.NoError(t, err) encryptedPEMKeyFilename2 := filepath.Join(tempDir, "encrypted_key2.key") err = ioutil.WriteFile(encryptedPEMKeyFilename2, encryptedPEMPrivKey2, 0644) @@ -1663,7 +1663,7 @@ func TestKeyRotationNonRoot(t *testing.T) { privKey, err := utils.GenerateECDSAKey(rand.Reader) require.NoError(t, err) - pemBytes, err := utils.EncryptPrivateKey(privKey, data.CanonicalTargetsRole, "", testPassphrase) + pemBytes, err := utils.ConvertPrivateKeyToPKCS8(privKey, data.CanonicalTargetsRole, "", testPassphrase) require.NoError(t, err) nBytes, err := tempFile.Write(pemBytes) @@ -1678,7 +1678,7 @@ func TestKeyRotationNonRoot(t *testing.T) { privKey2, err := utils.GenerateECDSAKey(rand.Reader) require.NoError(t, err) - pemBytes2, err := utils.KeyToPEM(privKey2, data.CanonicalTargetsRole, "") + pemBytes2, err := utils.ConvertPrivateKeyToPKCS8(privKey2, data.CanonicalTargetsRole, "", "") require.NoError(t, err) nBytes2, err := tempFile2.Write(pemBytes2) @@ -2506,7 +2506,7 @@ func TestClientKeyImport(t *testing.T) { privKey, err := utils.GenerateECDSAKey(rand.Reader) require.NoError(t, err) - pemBytes, err := utils.EncryptPrivateKey(privKey, data.CanonicalRootRole, "", "") + pemBytes, err := utils.ConvertPrivateKeyToPKCS8(privKey, data.CanonicalRootRole, "", "") require.NoError(t, err) nBytes, err := tempFile.Write(pemBytes) @@ -2534,7 +2534,7 @@ func TestClientKeyImport(t *testing.T) { privKey, err = utils.GenerateECDSAKey(rand.Reader) require.NoError(t, err) - pemBytes, err = utils.EncryptPrivateKey(privKey, "", "", "") + pemBytes, err = utils.ConvertPrivateKeyToPKCS8(privKey, "", "", "") require.NoError(t, err) nBytes, err = tempFile2.Write(pemBytes) @@ -2560,7 +2560,7 @@ func TestClientKeyImport(t *testing.T) { privKey, err = utils.GenerateECDSAKey(rand.Reader) require.NoError(t, err) - pemBytes, err = utils.EncryptPrivateKey(privKey, "", "", "") + pemBytes, err = utils.ConvertPrivateKeyToPKCS8(privKey, "", "", "") require.NoError(t, err) nBytes, err = tempFile3.Write(pemBytes) @@ -2590,7 +2590,7 @@ func TestClientKeyImport(t *testing.T) { privKey, err = utils.GenerateECDSAKey(rand.Reader) require.NoError(t, err) - pemBytes, err = utils.EncryptPrivateKey(privKey, "", "", "") + pemBytes, err = utils.ConvertPrivateKeyToPKCS8(privKey, "", "", "") require.NoError(t, err) nBytes, err = tempFile4.Write(pemBytes) @@ -2621,7 +2621,7 @@ func TestClientKeyImport(t *testing.T) { privKey, err = utils.GenerateECDSAKey(rand.Reader) require.NoError(t, err) - pemBytes, err = utils.EncryptPrivateKey(privKey, "", "", "") + pemBytes, err = utils.ConvertPrivateKeyToPKCS8(privKey, "", "", "") require.NoError(t, err) nBytes, err = tempFile5.Write(pemBytes) @@ -2652,7 +2652,7 @@ func TestClientKeyImport(t *testing.T) { privKey, err = utils.GenerateECDSAKey(rand.Reader) require.NoError(t, err) - pemBytes, err = utils.EncryptPrivateKey(privKey, data.CanonicalRootRole, "", testPassphrase) + pemBytes, err = utils.ConvertPrivateKeyToPKCS8(privKey, data.CanonicalRootRole, "", testPassphrase) require.NoError(t, err) nBytes, err = tempFile6.Write(pemBytes) @@ -2678,7 +2678,7 @@ func TestClientKeyImport(t *testing.T) { privKey, err = utils.GenerateECDSAKey(rand.Reader) require.NoError(t, err) - pemBytes, err = utils.EncryptPrivateKey(privKey, "", "", "") + pemBytes, err = utils.ConvertPrivateKeyToPKCS8(privKey, "", "", "") require.NoError(t, err) nBytes, err = tempFile7.Write(pemBytes) @@ -2708,7 +2708,7 @@ func TestClientKeyImport(t *testing.T) { privKey, err = utils.GenerateECDSAKey(rand.Reader) require.NoError(t, err) - pemBytes, err = utils.EncryptPrivateKey(privKey, data.CanonicalSnapshotRole, "", "") + pemBytes, err = utils.ConvertPrivateKeyToPKCS8(privKey, data.CanonicalSnapshotRole, "", "") require.NoError(t, err) nBytes, err = tempFile8.Write(pemBytes) @@ -2752,7 +2752,7 @@ func TestAddDelImportKeyPublishFlow(t *testing.T) { keyFile, err := ioutil.TempFile("", "pemfile") require.NoError(t, err) defer os.Remove(keyFile.Name()) - pemBytes, err := utils.EncryptPrivateKey(privKey, "", "", "") + pemBytes, err := utils.ConvertPrivateKeyToPKCS8(privKey, "", "", "") require.NoError(t, err) nBytes, err := keyFile.Write(pemBytes) require.NoError(t, err) @@ -3005,7 +3005,7 @@ func TestDelegationKeyImportExport(t *testing.T) { defer os.Remove(keyFile.Name()) privKey, err := utils.GenerateRSAKey(rand.Reader, 2048) require.NoError(t, err) - pemBytes, err := utils.EncryptPrivateKey(privKey, "", "", "") + pemBytes, err := utils.ConvertPrivateKeyToPKCS8(privKey, "", "", "") require.NoError(t, err) nBytes, err := keyFile.Write(pemBytes) require.NoError(t, err) diff --git a/cmd/notary/keys.go b/cmd/notary/keys.go index ea192bf5c..452f4a066 100644 --- a/cmd/notary/keys.go +++ b/cmd/notary/keys.go @@ -260,7 +260,7 @@ func generateKeyToFile(role, algorithm string, retriever notary.PassRetriever, o } if chosenPassphrase != "" { - pemPrivKey, err = tufutils.EncryptPrivateKey(privKey, data.RoleName(role), "", chosenPassphrase) + pemPrivKey, err = tufutils.ConvertPrivateKeyToPKCS8(privKey, data.RoleName(role), "", chosenPassphrase) if err != nil { return err } diff --git a/cryptoservice/crypto_service.go b/cryptoservice/crypto_service.go index 9cafb2816..ccff16ec6 100644 --- a/cryptoservice/crypto_service.go +++ b/cryptoservice/crypto_service.go @@ -1,12 +1,13 @@ package cryptoservice import ( - "fmt" - "crypto/x509" "encoding/pem" "errors" + "fmt" + "github.com/Sirupsen/logrus" + "github.com/docker/notary" "github.com/docker/notary/trustmanager" "github.com/docker/notary/tuf/data" "github.com/docker/notary/tuf/utils" @@ -143,9 +144,12 @@ func CheckRootKeyIsEncrypted(pemBytes []byte) error { return ErrNoValidPrivateKey } - if !x509.IsEncryptedPEMBlock(block) { - return ErrRootKeyNotEncrypted + if block.Type == "ENCRYPTED PRIVATE KEY" { + return nil + } + if !notary.FIPSEnabled() && x509.IsEncryptedPEMBlock(block) { + return nil } - return nil + return ErrRootKeyNotEncrypted } diff --git a/fips.go b/fips.go new file mode 100644 index 000000000..01ed2fb57 --- /dev/null +++ b/fips.go @@ -0,0 +1,13 @@ +package notary + +import "os" + +// FIPSEnvVar is the name of the environment variable that is being used to switch +// between FIPS and non-FIPS mode +const FIPSEnvVar = "GOFIPS" + +// FIPSEnabled returns true if environment variable `GOFIPS` has been set to enable +// FIPS mode +func FIPSEnabled() bool { + return os.Getenv(FIPSEnvVar) != "" +} diff --git a/fixtures/notary-server.key b/fixtures/notary-server.key index cd19c4be5..871d41f64 100644 --- a/fixtures/notary-server.key +++ b/fixtures/notary-server.key @@ -1,28 +1,28 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEogIBAAKCAQEAqNt5+U61Ws6/Qg54kYp+LkscfcOLQ7aeVm7wAhNIlsNk9Jeh -hz5ymumgRZidxsUULcL3jPnEFbm9wCoyEuznSaXkAOXzq6ZwuXYL+Zfw25meUh68 -wJvSYGJq8O1I9XcnkOo6T62uAoRez2DiHKHk6eHljkic87WUMn7ZwS1UYEyGF119 -ZFWVX1lRbE9hUJO3ovRsP1J7JclUHR2cWGvfEgJrKEOWGW9yNdU5NSx7Akuj8vae -rS973clvayYqKjbtkYTv1sIaokrbXf8U2p8CUZQ+SFhN9glNyCOFLWmWa2A3opkX -pFVe86sIEwQMzbJmrWYQ9aOhPS2fQyRYSsMA1wIDAQABAoIBAG6mtD1dCJajGM3u -sa+d86XebqMzOtV6nDPDqt+RR2YUUNm/a4g2sd817WLt6aZRizGZq6LkIUyjVObS -P9ILEF1AqjK0fYMkJIZEBwDeQmWFOyxRHBuTgL7Mf4u10rOYC4N5GhEQnRDlMUPw -FvvwUxO4hjdA+ijx+lVErulaDQq0yj5mL4LWu4cHm576OufzgHOIp6fQtfRVJIXD -W2ginblgYFLd+PPiM1RMPR/Pj63VWXWBn1VwLAxWN889E4VG2medl0taQgkNQ3/W -0J04KiTXPrtcUBy2AGoHikvN7gG7Up2IwRRbsXkUdhQNZ/HnIQlkFfteiqqt9VNR -Nsi31nECgYEA0qE+96TvYf8jeZsqrl8YQAvjXWrNA05eKZlT6cm6XpyXq22v9Cgn -2KXEhRwHZF2dQ2C+1PvboeTUbpdPX1nY2shY59L7+t68F/jxotcjx0yL+ZC742Fy -bWsc8Us0Ir2DD5g/+0F+LRLFJKSfJPdLzEkvwuYnlm6RcFlbxIxW6h0CgYEAzTrE -6ulEhN0fKeJY/UaK/8GlLllXc2Z5t7mRicN1s782l5qi0n1R57VJw/Ezx4JN1mcQ -4axe9zzjAA5JfSDfyTyNedP1KOmCaKmBqGa9JppxGcVQpMDg8+QvYnJ8o5JXEXSE -TOnpY4RTEA1RGnA5KbbJ7R1MiHUGXC9nizVHxIMCgYB8cu1DYN5XpmoNddK4CFPJ -s7x4+5t6MpmMNp3P6nMFZ7xte3eU6QzyAq+kfjUX5f//SXA3Y0AX3Z5uYVRyYCGy -0uFEx/I9/dBg0aPjtP3cyauCnzOEW5VCdSE6qFZ7mEGRu0FCcSXd99MnnWSycLMG -Vs+zdk05osan/QQtk0XfOQKBgDfkIWy4SmjEr5AAjKutYn10hz+wJRjQd6WJbBFQ -oeVp1bxD6MPaTUwFGym5rphO7FPPjdFn2BUNB+Uj/u+M3GU5kG31Q3b44QMP5reu -AyVYOiUCj4vO23SQWDc/ZqJFYGDokn8/1Me9acGdXtEMbwTlOujQad9fv3OrlU9c -G0dxAoGAHcntflD6UvQ5/PYOirNJL1GhSspF7u72NrsYjaoZls83uIqucJiB5hMH -Ovq1TJbl0DwDBOyMmt5gZraPQB0P5/5GvnxqGlIAKIwi2VuQ2XHpSBE8Pg5Pveb8 -sgFLFnwL5+JyqOP65AV3Eh5b4BJc6kqKz4gVmKLBQeo6lE13sNs= ------END RSA PRIVATE KEY----- - +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCo23n5TrVazr9C +DniRin4uSxx9w4tDtp5WbvACE0iWw2T0l6GHPnKa6aBFmJ3GxRQtwveM+cQVub3A +KjIS7OdJpeQA5fOrpnC5dgv5l/DbmZ5SHrzAm9JgYmrw7Uj1dyeQ6jpPra4ChF7P +YOIcoeTp4eWOSJzztZQyftnBLVRgTIYXXX1kVZVfWVFsT2FQk7ei9Gw/UnslyVQd +HZxYa98SAmsoQ5YZb3I11Tk1LHsCS6Py9p6tL3vdyW9rJioqNu2RhO/WwhqiSttd +/xTanwJRlD5IWE32CU3II4UtaZZrYDeimRekVV7zqwgTBAzNsmatZhD1o6E9LZ9D +JFhKwwDXAgMBAAECggEAbqa0PV0IlqMYze6xr53zpd5uozM61XqcM8Oq35FHZhRQ +2b9riDax3zXtYu3pplGLMZmrouQhTKNU5tI/0gsQXUCqMrR9gyQkhkQHAN5CZYU7 +LFEcG5OAvsx/i7XSs5gLg3kaERCdEOUxQ/AW+/BTE7iGN0D6KPH6VUSu6VoNCrTK +PmYvgta7hwebnvo65/OAc4inp9C19FUkhcNbaCKduWBgUt348+IzVEw9H8+PrdVZ +dYGfVXAsDFY3zz0ThUbaZ52XS1pCCQ1Df9bQnTgqJNc+u1xQHLYAageKS83uAbtS +nYjBFFuxeRR2FA1n8echCWQV+16Kqq31U1E2yLfWcQKBgQDSoT73pO9h/yN5myqu +XxhAC+Ndas0DTl4pmVPpybpenJerba/0KCfYpcSFHAdkXZ1DYL7U+9uh5NRul09f +WdjayFjn0vv63rwX+PGi1yPHTIv5kLvjYXJtaxzxSzQivYMPmD/7QX4tEsUkpJ8k +90vMSS/C5ieWbpFwWVvEjFbqHQKBgQDNOsTq6USE3R8p4lj9Ror/waUuWVdzZnm3 +uZGJw3WzvzaXmqLSfVHntUnD8TPHgk3WZxDhrF73POMADkl9IN/JPI150/Uo6YJo +qYGoZr0mmnEZxVCkwODz5C9icnyjklcRdIRM6eljhFMQDVEacDkptsntHUyIdQZc +L2eLNUfEgwKBgHxy7UNg3lemag110rgIU8mzvHj7m3oymYw2nc/qcwVnvG17d5Tp +DPICr6R+NRfl//9JcDdjQBfdnm5hVHJgIbLS4UTH8j390GDRo+O0/dzJq4KfM4Rb +lUJ1ITqoVnuYQZG7QUJxJd330yedZLJwswZWz7N2TTmixqf9BC2TRd85AoGAN+Qh +bLhKaMSvkACMq61ifXSHP7AlGNB3pYlsEVCh5WnVvEPow9pNTAUbKbmumE7sU8+N +0WfYFQ0H5SP+74zcZTmQbfVDdvjhAw/mt64DJVg6JQKPi87bdJBYNz9mokVgYOiS +fz/Ux71pwZ1e0QxvBOU66NBp31+/c6uVT1wbR3ECgYAdye1+UPpS9Dn89g6Ks0kv +UaFKykXu7vY2uxiNqhmWzze4iq5wmIHmEwc6+rVMluXQPAME7Iya3mBmto9AHQ/n +/ka+fGoaUgAojCLZW5DZcelIETw+Dk+95vyyAUsWfAvn4nKo4/rkBXcSHlvgElzq +SorPiBWYosFB6jqUTXew2w== +-----END PRIVATE KEY----- diff --git a/fixtures/notary-signer.key b/fixtures/notary-signer.key index 2db6e2ce9..eec675f42 100644 --- a/fixtures/notary-signer.key +++ b/fixtures/notary-signer.key @@ -1,28 +1,28 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEA2E7z4r3FPoz11AL3QfGzpuZNdZDMTmhXaQt5Uqo6PAC8a3rA -ETZckIBtNLcU5Eg7Kg5VANUK/Y+TaVlcZapKsfxJja6aNEkiE8OKo31Xkz+ByYyb -YRSDCanaXhuwOTUxDoZJu53mSNgZlyn25fEBU7y18tUtAXuiEliqJ7Ek359mRDLF -OBKqsjsworWlS8Zf99roRfOkrDFNA8leIb9lBbQ+f6B2vP78J/Q9xXJX8aFzZFH5 -aMrOqoCINQmgS0qb/FBVFxI9tqBUsJ7QpmvtWa0NacexS/1kH0FE2UiVFUM6FUMI -Jwy7/MS1zG4fyNrt9p+LnE23q8IGYe4JdC1NSwIDAQABAoIBAHykYhyRxYrZpv3Y -B6pUIHVX1+Ka4V98+IFrPynHNW9F7UzxmqNQc95AYq0xojQ4+v6s64ZjPMYHaaYW -/AsJKamN+sRNjEX8rko9LzIuE7yhp6QABbjXHPsAiPgZdF5CrFX2Q558yinHfFeC -sualDWK3JxEajaiBGU8BEGt2xAymuWACGblrM1aAEZa8B84TW3CzzcdyzAkn8P3e -piJCe+DWMc33441r0KlV5GruwF9ewXiWzZtXAOiP/0xEDICFdlFWbO39myMpxDdU -Y0uZ+zmn2G3gz2tz25thH0Wl7mDQ3AA0VlHurgPBBEekeZPQmjiKW+F4slCzXvuy -kW/urIECgYEA/LhY+OWlZVXzIEly7z1/cU9/WImqTs2uRKDeQHMwZrd7D9BXkJuQ -jPN+jZlMYBBrxoaCywbMrgB80Z3MgGHaSx9OIDEZmaxyuQv0zQJCMogysYkbCcaD -mHYnyAf7OXa708Z168WAisEhrwa/DXBn3/hPoBkrbMsuPF/J+tEP7lsCgYEA2x2g -86SitgPVeNV3iuZ6D/SV0QIbDWOYoST2GQn2LnfALIOrzpXRClOSQZ2pGtg9gYo1 -owUyyOSv2Fke93p3ufHv3Gqvjl55lzBVV0siHkEXwHcol36DDGQcskVnXJqaL3IF -tiOisuJS9A7PW7gEi0miyGzzB/kh/IEWHKqLL9ECgYEAoBOFB+MuqMmQftsHWlLx -7qwUVdidb90IjZ/4J4rPFcESyimFzas8HIv/lWGM5yx/l/iL0F42N+FHLt9tMcTJ -qNvjeLChLp307RGNtm2/0JJEyf+2iLKdmGz/Nc0YbIWw46vJ9dXcXgeHdn4ndjPF -GDEI/rfysa7hUoy6O41BMhECgYBPJsLPgHdufLAOeD44pM0PGnFMERCoo4OtImbr -4JdXbdazvdTASYo7yriYj1VY5yhAtSZu/x+7RjDnXDo9d7XsK6NT4g4Mxb/yh3ks -kW1/tE/aLLEzGHZKcZeUJlISN57e6Ld7dh/9spf4pajuHuk1T6JH+GNKTAqk5hSQ -wmKJIQKBgCGBWGvJrCeT5X9oHdrlHj2YoKvIIG1eibagcjcKemD7sWzi7Q4P7JIo -xeX8K1WVxdBpo4/RiQcGFmwSmSUKwwr1dO00xtjxIl7ip4DU+WAM7CdmcOIOMbr4 -rP9T/wy1ZBkERCIw2ElybTzB8yuOlNLuOMhUeU55xUMFNYYrWEp2 ------END RSA PRIVATE KEY----- - +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDYTvPivcU+jPXU +AvdB8bOm5k11kMxOaFdpC3lSqjo8ALxresARNlyQgG00txTkSDsqDlUA1Qr9j5Np +WVxlqkqx/EmNrpo0SSITw4qjfVeTP4HJjJthFIMJqdpeG7A5NTEOhkm7neZI2BmX +Kfbl8QFTvLXy1S0Be6ISWKonsSTfn2ZEMsU4EqqyOzCitaVLxl/32uhF86SsMU0D +yV4hv2UFtD5/oHa8/vwn9D3FclfxoXNkUfloys6qgIg1CaBLSpv8UFUXEj22oFSw +ntCma+1ZrQ1px7FL/WQfQUTZSJUVQzoVQwgnDLv8xLXMbh/I2u32n4ucTberwgZh +7gl0LU1LAgMBAAECggEAfKRiHJHFitmm/dgHqlQgdVfX4prhX3z4gWs/Kcc1b0Xt +TPGao1Bz3kBirTGiNDj6/qzrhmM8xgdpphb8CwkpqY36xE2MRfyuSj0vMi4TvKGn +pAAFuNcc+wCI+Bl0XkKsVfZDnnzKKcd8V4Ky5qUNYrcnERqNqIEZTwEQa3bEDKa5 +YAIZuWszVoARlrwHzhNbcLPNx3LMCSfw/d6mIkJ74NYxzffjjWvQqVXkau7AX17B +eJbNm1cA6I//TEQMgIV2UVZs7f2bIynEN1RjS5n7OafYbeDPa3Pbm2EfRaXuYNDc +ADRWUe6uA8EER6R5k9CaOIpb4XiyULNe+7KRb+6sgQKBgQD8uFj45aVlVfMgSXLv +PX9xT39YiapOza5EoN5AczBmt3sP0FeQm5CM836NmUxgEGvGhoLLBsyuAHzRncyA +YdpLH04gMRmZrHK5C/TNAkIyiDKxiRsJxoOYdifIB/s5drvTxnXrxYCKwSGvBr8N +cGff+E+gGStsyy48X8n60Q/uWwKBgQDbHaDzpKK2A9V41XeK5noP9JXRAhsNY5ih +JPYZCfYud8Asg6vOldEKU5JBnaka2D2BijWjBTLI5K/YWR73ene58e/caq+OXnmX +MFVXSyIeQRfAdyiXfoMMZByyRWdcmpovcgW2I6Ky4lL0Ds9buASLSaLIbPMH+SH8 +gRYcqosv0QKBgQCgE4UH4y6oyZB+2wdaUvHurBRV2J1v3QiNn/gnis8VwRLKKYXN +qzwci/+VYYznLH+X+IvQXjY34Ucu320xxMmo2+N4sKEunfTtEY22bb/QkkTJ/7aI +sp2YbP81zRhshbDjq8n11dxeB4d2fid2M8UYMQj+t/KxruFSjLo7jUEyEQKBgE8m +ws+Ad258sA54PjikzQ8acUwREKijg60iZuvgl1dt1rO91MBJijvKuJiPVVjnKEC1 +Jm7/H7tGMOdcOj13tewro1PiDgzFv/KHeSyRbX+0T9ossTMYdkpxl5QmUhI3nt7o +t3t2H/2yl/ilqO4e6TVPokf4Y0pMCqTmFJDCYokhAoGAIYFYa8msJ5Plf2gd2uUe +PZigq8ggbV6JtqByNwp6YPuxbOLtDg/skijF5fwrVZXF0Gmjj9GJBwYWbBKZJQrD +CvV07TTG2PEiXuKngNT5YAzsJ2Zw4g4xuvis/1P/DLVkGQREIjDYSXJtPMHzK46U +0u44yFR5TnnFQwU1hitYSnY= +-----END PRIVATE KEY----- diff --git a/fixtures/secure.example.com.key b/fixtures/secure.example.com.key index 345f7fede..f35b10bb0 100644 --- a/fixtures/secure.example.com.key +++ b/fixtures/secure.example.com.key @@ -1,28 +1,28 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAmLYiYCTAWJBWAuxZLqVmV4FiUdGgEqoQvCbN73zF/mQfhq0C -ITo6xSxs1QiGDOzUtkpzXzziSj4J5+et4JkFleeEKaMcHadeIsSlHGvVtXDv93oR -3ydmfZO+ULRU8xHloqcLr1KrOP1daLfdMRbactd75UQgvw9XTsdeMVX5AlicSENV -KV+AQXvVpv8PT10MSvlBFam4reXuY/SkeMbIaW5pFu6AQv3Zmftt2ta0CB9kb1mY -d+OKru8Hnnq5aJw6R3GhP0TBd25P1PkiSxM2KGYZZk0W/NZqLK9/LTFKTNCv7VjC -bysVo7HxCY0bQe/bDP82v7SnLtb3aZogfva4HQIDAQABAoIBAQCLPj+X5MrRtkIH -BlTHGJ95mIr6yaYofpMlzEgoX1/1dnvcg/IWNA8UbE6L7Oq17FiEItyR8WTwhyLn -JrO/wCd8qQ40HPrs+wf1sdJPWPATMfhMcizLihSE2mtFETkILcByD9iyszFWlIdQ -jZ4NPaZP4rWgtf8Z1zYnqdf0Kk0T2imFya0qyoRLo40kxeb4p5K53JD7rPLQNyvO -YeFXTuKxBrFEMs6/wFjl+TO4nfHQXQlgQp4MNd9L5fEQBj+TvGVX+zcQEmzxljK8 -zFNXyxvXgjBPD+0V7yRhTYjrUfZJ4RX1yKDpdsva6BXL7t9hNEg/aGnKRDYF3i5q -WQz8csCBAoGBAMfdtAr3RCuCxe0TIVBon5wubau6HLOxorcXSvxO5PO2kzhy3+GY -xcCMJ+Wo0dTFXjQD3oxRKuDrPRK7AX/grYn7qJo6W7SM9xYEq3HspJJFGkcRsvem -MALt8bvG5NkGmLJD+pTOKVaTZRjW3BM6GcMzBgsLynQcLllRtNI8Hcw9AoGBAMOa -CMsWQfoOUjUffrXN0UnXLEPEeazPobnCHVtE244FdX/BFu5WMA7qqaPRyvnfK0Vl -vF5sGNiBCOnq1zjYee6FD2eyAzVmWJXM1DB4Ewp4ZaABS0ZCZgNfyd1badY4IZpw -pjYEQprguw+J8yZItNJRo+WBmnSgZy6o1bpDaflhAoGAYf61GS9VkFPlQbFAg1FY -+NXW1f1Bt2VgV48nKAByx3/8PRAt70ndo+PUaAlXIJDI+I3xHzFo6bDNWBKy0IVT -8TSf3UbB0gvP1k7h1NDnfAQ/txrZeg1Uuwr5nE0Pxc0zLyyffzh6EkXgqsYmT5MM -MKYiz2WvlTCAFTE3jGEHZy0CgYBti/cgxnZs9VhVKC5u47YzBK9lxMPgZOjOgEiw -tP/Bqo0D38BX+y0vLX2UogprpvE1DKVSvHetyZaUa1HeJF8llp/qE2h4n7k9LFoq -SxVe588CrbbawpUfjqYfsvKzZvxq4mw0FG65DuO08C2dY1rh75c7EjrO1obzOtt4 -VgkkAQKBgDnRyLnzlMfvjCyW9+cHbURQNe2iupfnlrXWEntg56USBVrFtfRQxDRp -fBtlq+0BNfDVdoVNasTCBW16UKoRBH1/k5idz5QPEbKY2055sNxHMVg0uzdb4HXr -73uaYzNrT8P7wyHFF3UL5bd0aO5DT1VYvGlHHgOhCyqcM+RBgPBS ------END RSA PRIVATE KEY----- - +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCYtiJgJMBYkFYC +7FkupWZXgWJR0aASqhC8Js3vfMX+ZB+GrQIhOjrFLGzVCIYM7NS2SnNfPOJKPgnn +563gmQWV54Qpoxwdp14ixKUca9W1cO/3ehHfJ2Z9k75QtFTzEeWipwuvUqs4/V1o +t90xFtpy13vlRCC/D1dOx14xVfkCWJxIQ1UpX4BBe9Wm/w9PXQxK+UEVqbit5e5j +9KR4xshpbmkW7oBC/dmZ+23a1rQIH2RvWZh344qu7weeerlonDpHcaE/RMF3bk/U ++SJLEzYoZhlmTRb81mosr38tMUpM0K/tWMJvKxWjsfEJjRtB79sM/za/tKcu1vdp +miB+9rgdAgMBAAECggEBAIs+P5fkytG2QgcGVMcYn3mYivrJpih+kyXMSChfX/V2 +e9yD8hY0DxRsTovs6rXsWIQi3JHxZPCHIucms7/AJ3ypDjQc+uz7B/Wx0k9Y8BMx ++ExyLMuKFITaa0UROQgtwHIP2LKzMVaUh1CNng09pk/itaC1/xnXNiep1/QqTRPa +KYXJrSrKhEujjSTF5vinkrnckPus8tA3K85h4VdO4rEGsUQyzr/AWOX5M7id8dBd +CWBCngw130vl8RAGP5O8ZVf7NxASbPGWMrzMU1fLG9eCME8P7RXvJGFNiOtR9knh +FfXIoOl2y9roFcvu32E0SD9oacpENgXeLmpZDPxywIECgYEAx920CvdEK4LF7RMh +UGifnC5tq7ocs7GitxdK/E7k87aTOHLf4ZjFwIwn5ajR1MVeNAPejFEq4Os9ErsB +f+CtifuomjpbtIz3FgSrceykkkUaRxGy96YwAu3xu8bk2QaYskP6lM4pVpNlGNbc +EzoZwzMGCwvKdBwuWVG00jwdzD0CgYEAw5oIyxZB+g5SNR9+tc3RSdcsQ8R5rM+h +ucIdW0TbjgV1f8EW7lYwDuqpo9HK+d8rRWW8XmwY2IEI6erXONh57oUPZ7IDNWZY +lczUMHgTCnhloAFLRkJmA1/J3Vtp1jghmnCmNgRCmuC7D4nzJki00lGj5YGadKBn +LqjVukNp+WECgYBh/rUZL1WQU+VBsUCDUVj41dbV/UG3ZWBXjycoAHLHf/w9EC3v +Sd2j49RoCVcgkMj4jfEfMWjpsM1YErLQhVPxNJ/dRsHSC8/WTuHU0Od8BD+3Gtl6 +DVS7CvmcTQ/FzTMvLJ9/OHoSReCqxiZPkwwwpiLPZa+VMIAVMTeMYQdnLQKBgG2L +9yDGdmz1WFUoLm7jtjMEr2XEw+Bk6M6ASLC0/8GqjQPfwFf7LS8tfZSiCmum8TUM +pVK8d63JlpRrUd4kXyWWn+oTaHifuT0sWipLFV7nzwKtttrClR+Oph+y8rNm/Gri +bDQUbrkO47TwLZ1jWuHvlzsSOs7WhvM623hWCSQBAoGAOdHIufOUx++MLJb35wdt +RFA17aK6l+eWtdYSe2DnpRIFWsW19FDENGl8G2Wr7QE18NV2hU1qxMIFbXpQqhEE +fX+TmJ3PlA8RspjbTnmw3EcxWDS7N1vgdevve5pjM2tPw/vDIcUXdQvlt3Ro7kNP +VVi8aUceA6ELKpwz5EGA8FI= +-----END PRIVATE KEY----- diff --git a/trustmanager/keystore.go b/trustmanager/keystore.go index 2049a3ef7..9f4760488 100644 --- a/trustmanager/keystore.go +++ b/trustmanager/keystore.go @@ -1,7 +1,6 @@ package trustmanager import ( - "encoding/pem" "fmt" "path/filepath" "strings" @@ -114,11 +113,7 @@ func (s *GenericKeyStore) AddKey(keyInfo KeyInfo, privKey data.PrivateKey) error } } - if chosenPassphrase != "" { - pemPrivKey, err = utils.EncryptPrivateKey(privKey, keyInfo.Role, keyInfo.Gun, chosenPassphrase) - } else { - pemPrivKey, err = utils.KeyToPEM(privKey, keyInfo.Role, keyInfo.Gun) - } + pemPrivKey, err = utils.ConvertPrivateKeyToPKCS8(privKey, keyInfo.Role, keyInfo.Gun, chosenPassphrase) if err != nil { return err @@ -204,11 +199,11 @@ func copyKeyInfoMap(keyInfoMap map[string]KeyInfo) map[string]KeyInfo { func KeyInfoFromPEM(pemBytes []byte, filename string) (string, KeyInfo, error) { var keyID string keyID = filepath.Base(filename) - block, _ := pem.Decode(pemBytes) - if block == nil { - return "", KeyInfo{}, fmt.Errorf("could not decode PEM block for key %s", filename) + role, gun, err := utils.ExtractPrivateKeyAttributes(pemBytes) + if err != nil { + return "", KeyInfo{}, err } - return keyID, KeyInfo{Gun: data.GUN(block.Headers["gun"]), Role: data.RoleName(block.Headers["role"])}, nil + return keyID, KeyInfo{Gun: gun, Role: role}, nil } // getKeyRole finds the role for the given keyID. It attempts to look @@ -224,10 +219,12 @@ func getKeyRole(s Storage, keyID string) (data.RoleName, error) { if err != nil { return "", err } - block, _ := pem.Decode(d) - if block != nil { - return data.RoleName(block.Headers["role"]), nil + + role, _, err := utils.ExtractPrivateKeyAttributes(d) + if err != nil { + return "", err } + return role, nil } } return "", ErrKeyNotFound{KeyID: keyID} diff --git a/trustmanager/keystore_test.go b/trustmanager/keystore_test.go index 8c876d04a..a287181c6 100644 --- a/trustmanager/keystore_test.go +++ b/trustmanager/keystore_test.go @@ -2,8 +2,8 @@ package trustmanager import ( "crypto/rand" + "encoding/pem" "errors" - "fmt" "io/ioutil" "os" "path/filepath" @@ -58,7 +58,7 @@ func testAddKeyWithRole(t *testing.T, role data.RoleName) { // Check to see if file exists b, err := ioutil.ReadFile(expectedFilePath) require.NoError(t, err, "expected file not found") - require.Contains(t, string(b), "-----BEGIN EC PRIVATE KEY-----") + require.Contains(t, string(b), "-----BEGIN ENCRYPTED PRIVATE KEY-----") // Check that we have the role and gun info for this key's ID keyInfo, ok := store.keyInfoMap[privKey.ID()] @@ -91,9 +91,9 @@ func TestKeyStoreInternalState(t *testing.T) { var privKeyPEM []byte // generate the correct PEM role header if role == data.CanonicalRootRole || data.IsDelegation(role) || !data.ValidRole(role) { - privKeyPEM, err = utils.KeyToPEM(privKey, role, "") + privKeyPEM, err = utils.ConvertPrivateKeyToPKCS8(privKey, role, "", "") } else { - privKeyPEM, err = utils.KeyToPEM(privKey, role, gun) + privKeyPEM, err = utils.ConvertPrivateKeyToPKCS8(privKey, role, gun, "") } require.NoError(t, err, "could not generate PEM") @@ -176,71 +176,45 @@ func TestGet(t *testing.T) { } func testGetKeyWithRole(t *testing.T, gun data.GUN, role data.RoleName) { - var testData []byte - if gun == "" { - testData = []byte(fmt.Sprintf(`-----BEGIN RSA PRIVATE KEY----- -role: %s + var testPEM []byte + testPEM = []byte(`-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC2cL8WamG24ihl +JSVG8ZVel05lPqYD0S8ol1L+zzwsHkim2DS+a5BLX5+QJtCfZrR+Pzo+4pCrjU+N +R/71aYNm/M95h/JSJxdEoTgYCCHNJD8IYpTc6lXyy49lSQh7svLpZ2dQwHoGB5VC +tpsh8xvLLbXfk/G7ihEeZqG7/Tnoe+uotkiODOTjxiTGvQQjoAc4hQgzGH4sjC7U +8E8zB0j1BQWM/fhRX/ww3V/SRB2T1u0aAurF1BnUdDazZMBxWQ7DxmY3FNbeNXqf +KKeQMN1Rodu8hJw0gxL1hbOWmcYksmGZfPDzYXiHBdscCFr/wimOl9BO/o2xbV5+ +phbph9cFAgMBAAECggEBAIAcA9L1uM/3V25O+zIqCj11+jLWHzWm+nqCaGFNnG9O +hK3EPKVKWvTSnPVYjD6inDPaqkfmSLhubmJDICGsif0ToY0xjVNq58flfcJCU5n9 +zdVRhD7svpXTo0n4UuCp9DE5zy7BOe5p/MHwAFeCow21d3UcKi8K8KJsZz3ev38j +9Y8ASd24NcyZfE4mnjDjA/MuzlPoQYMwAh4f3mrEKu5v9dCT+m70lJTzSNAc4gD0 +93mMkGRsUKjvZyCu/IlXncBczaSVovX5IGdiGPa7Qk+CP9r+PGQUasb+e5o7VMzh +xyjIrCV1u48vRyJsc7xrZ+PUkVk74u9mQ3wxQXNzi7ECgYEA5BftyMlzv2oqAzQg +isS0f616qX5YmRK/riC/4+HRaXEsA/LiI8tuW04vdgcelUqxo1TFpv+J4z16ItF5 +kscb6ev9wsFa0VInsvI3hqZ8e4AuqlvU8Rii1anxkbwE5mstRgeR9p410+0T2GiW +JaWVy8mxsneVI0sdR5ooJ+ZBQpcCgYEAzMLtV52aQvnCLPejPI+fBnOjoLXTVaaB +xqZWfOzuozjYVlqSUsKbKbMVtIy+rPIJt26/qw8i6V8Dx2HlUcySU5fAumpWigK4 +Dh64eZ+yJrQeqgRJoLoZhTbgxe4fv7+f649WcipwD0ptEaqjD11Wdr0973tw0wdc +Pqn9SlPoksMCgYBqUKj5xMRZvQ82DQ75/3Oua1rYM9byCmYjsIogmrn0Ltb4RDaZ +vpGCp2/B0NG1fmpMGhBCpatMqvQJ1J+ZBYuCPgg6xcsh8+wjIXk2HtW47udRappX +gkcr1hmN9xhFmkEw+ghT7ixiyodMgHszsvmeUjWsXMa7+5/7JuR+rHlQowKBgE0T +Lr3lMDT3yJSeno5kTWrjSntrFeLOq1j4MeQSV32PHzfaHewTHs7if1AYDooRDYFD +qdgc+Xo47rY1blmNFKNsovpInsySW2/NNolpiGizMjuzI3fhtUuErbUzfjXyTqMf +sF2HBelrjYSx43EcJDjL4S1tHLoCskFQQWyiCxB7AoGBANSohPiPmJLvCEmZTdHm +KcRNz9jE0wO5atCZADIfuOrYHYTQk3YTI5V3GviUNLdmbw4TQChwAgAYVNth1rpL +5jSqfF3RtNBePZixG2WzxYd2ZwvJxvKa33i1E8UfM+yEZH4Gc5ukDt28m0fyFBmi +QvS5quTEllrvrVuWfhpsjl/l +-----END PRIVATE KEY----- +`) + testBlock, _ := pem.Decode(testPEM) + require.NotEmpty(t, testBlock, "could not decode pem") -MIIEogIBAAKCAQEAyUIXjsrWRrvPa4Bzp3VJ6uOUGPay2fUpSV8XzNxZxIG/Opdr -+k3EQi1im6WOqF3Y5AS1UjYRxNuRN+cAZeo3uS1pOTuoSupBXuchVw8s4hZJ5vXn -TRmGb+xY7tZ1ZVgPfAZDib9sRSUsL/gC+aSyprAjG/YBdbF06qKbfOfsoCEYW1OQ -82JqHzQH514RFYPTnEGpvfxWaqmFQLmv0uMxV/cAYvqtrGkXuP0+a8PknlD2obw5 -0rHE56Su1c3Q42S7L51K38tpbgWOSRcTfDUWEj5v9wokkNQvyKBwbS996s4EJaZd -7r6M0h1pHnuRxcSaZLYRwgOe1VNGg2VfWzgd5QIDAQABAoIBAF9LGwpygmj1jm3R -YXGd+ITugvYbAW5wRb9G9mb6wspnwNsGTYsz/UR0ZudZyaVw4jx8+jnV/i3e5PC6 -QRcAgqf8l4EQ/UuThaZg/AlT1yWp9g4UyxNXja87EpTsGKQGwTYxZRM4/xPyWOzR -mt8Hm8uPROB9aA2JG9npaoQG8KSUj25G2Qot3ukw/IOtqwN/Sx1EqF0EfCH1K4KU -a5TrqlYDFmHbqT1zTRec/BTtVXNsg8xmF94U1HpWf3Lpg0BPYT7JiN2DPoLelRDy -a/A+a3ZMRNISL5wbq/jyALLOOyOkIqa+KEOeW3USuePd6RhDMzMm/0ocp5FCwYfo -k4DDeaECgYEA0eSMD1dPGo+u8UTD8i7ZsZCS5lmXLNuuAg5f5B/FGghD8ymPROIb -dnJL5QSbUpmBsYJ+nnO8RiLrICGBe7BehOitCKi/iiZKJO6edrfNKzhf4XlU0HFl -jAOMa975pHjeCoZ1cXJOEO9oW4SWTCyBDBSqH3/ZMgIOiIEk896lSmkCgYEA9Xf5 -Jqv3HtQVvjugV/axAh9aI8LMjlfFr9SK7iXpY53UdcylOSWKrrDok3UnrSEykjm7 -UL3eCU5jwtkVnEXesNn6DdYo3r43E6iAiph7IBkB5dh0yv3vhIXPgYqyTnpdz4pg -3yPGBHMPnJUBThg1qM7k6a2BKHWySxEgC1DTMB0CgYAGvdmF0J8Y0k6jLzs/9yNE -4cjmHzCM3016gW2xDRgumt9b2xTf+Ic7SbaIV5qJj6arxe49NqhwdESrFohrKaIP -kM2l/o2QaWRuRT/Pvl2Xqsrhmh0QSOQjGCYVfOb10nAHVIRHLY22W4o1jk+piLBo -a+1+74NRaOGAnu1J6/fRKQKBgAF180+dmlzemjqFlFCxsR/4G8s2r4zxTMXdF+6O -3zKuj8MbsqgCZy7e8qNeARxwpCJmoYy7dITNqJ5SOGSzrb2Trn9ClP+uVhmR2SH6 -AlGQlIhPn3JNzI0XVsLIloMNC13ezvDE/7qrDJ677EQQtNEKWiZh1/DrsmHr+irX -EkqpAoGAJWe8PC0XK2RE9VkbSPg9Ehr939mOLWiHGYTVWPttUcum/rTKu73/X/mj -WxnPWGtzM1pHWypSokW90SP4/xedMxludvBvmz+CTYkNJcBGCrJumy11qJhii9xp -EMl3eFOJXjIch/wIesRSN+2dGOsl7neercjMh1i9RvpCwHDx/E0= ------END RSA PRIVATE KEY----- -`, role)) - } else { - testData = []byte(fmt.Sprintf(`-----BEGIN RSA PRIVATE KEY----- -gun: %s -role: %s + testPrivKey, err := utils.ParsePKCS8ToTufKey(testBlock.Bytes, nil) + require.NoError(t, err, "could not parse pkcs8 key") + + testData, err := utils.ConvertPrivateKeyToPKCS8(testPrivKey, role, gun, "") + require.NoError(t, err, "could not wrap pkcs8 key") -MIIEogIBAAKCAQEAyUIXjsrWRrvPa4Bzp3VJ6uOUGPay2fUpSV8XzNxZxIG/Opdr -+k3EQi1im6WOqF3Y5AS1UjYRxNuRN+cAZeo3uS1pOTuoSupBXuchVw8s4hZJ5vXn -TRmGb+xY7tZ1ZVgPfAZDib9sRSUsL/gC+aSyprAjG/YBdbF06qKbfOfsoCEYW1OQ -82JqHzQH514RFYPTnEGpvfxWaqmFQLmv0uMxV/cAYvqtrGkXuP0+a8PknlD2obw5 -0rHE56Su1c3Q42S7L51K38tpbgWOSRcTfDUWEj5v9wokkNQvyKBwbS996s4EJaZd -7r6M0h1pHnuRxcSaZLYRwgOe1VNGg2VfWzgd5QIDAQABAoIBAF9LGwpygmj1jm3R -YXGd+ITugvYbAW5wRb9G9mb6wspnwNsGTYsz/UR0ZudZyaVw4jx8+jnV/i3e5PC6 -QRcAgqf8l4EQ/UuThaZg/AlT1yWp9g4UyxNXja87EpTsGKQGwTYxZRM4/xPyWOzR -mt8Hm8uPROB9aA2JG9npaoQG8KSUj25G2Qot3ukw/IOtqwN/Sx1EqF0EfCH1K4KU -a5TrqlYDFmHbqT1zTRec/BTtVXNsg8xmF94U1HpWf3Lpg0BPYT7JiN2DPoLelRDy -a/A+a3ZMRNISL5wbq/jyALLOOyOkIqa+KEOeW3USuePd6RhDMzMm/0ocp5FCwYfo -k4DDeaECgYEA0eSMD1dPGo+u8UTD8i7ZsZCS5lmXLNuuAg5f5B/FGghD8ymPROIb -dnJL5QSbUpmBsYJ+nnO8RiLrICGBe7BehOitCKi/iiZKJO6edrfNKzhf4XlU0HFl -jAOMa975pHjeCoZ1cXJOEO9oW4SWTCyBDBSqH3/ZMgIOiIEk896lSmkCgYEA9Xf5 -Jqv3HtQVvjugV/axAh9aI8LMjlfFr9SK7iXpY53UdcylOSWKrrDok3UnrSEykjm7 -UL3eCU5jwtkVnEXesNn6DdYo3r43E6iAiph7IBkB5dh0yv3vhIXPgYqyTnpdz4pg -3yPGBHMPnJUBThg1qM7k6a2BKHWySxEgC1DTMB0CgYAGvdmF0J8Y0k6jLzs/9yNE -4cjmHzCM3016gW2xDRgumt9b2xTf+Ic7SbaIV5qJj6arxe49NqhwdESrFohrKaIP -kM2l/o2QaWRuRT/Pvl2Xqsrhmh0QSOQjGCYVfOb10nAHVIRHLY22W4o1jk+piLBo -a+1+74NRaOGAnu1J6/fRKQKBgAF180+dmlzemjqFlFCxsR/4G8s2r4zxTMXdF+6O -3zKuj8MbsqgCZy7e8qNeARxwpCJmoYy7dITNqJ5SOGSzrb2Trn9ClP+uVhmR2SH6 -AlGQlIhPn3JNzI0XVsLIloMNC13ezvDE/7qrDJ677EQQtNEKWiZh1/DrsmHr+irX -EkqpAoGAJWe8PC0XK2RE9VkbSPg9Ehr939mOLWiHGYTVWPttUcum/rTKu73/X/mj -WxnPWGtzM1pHWypSokW90SP4/xedMxludvBvmz+CTYkNJcBGCrJumy11qJhii9xp -EMl3eFOJXjIch/wIesRSN+2dGOsl7neercjMh1i9RvpCwHDx/E0= ------END RSA PRIVATE KEY----- -`, gun, role)) - } testName := "keyID" testExt := "key" perms := os.FileMode(0755) @@ -266,7 +240,7 @@ EMl3eFOJXjIch/wIesRSN+2dGOsl7neercjMh1i9RvpCwHDx/E0= privKey, _, err := store.GetKey(testName) require.NoError(t, err, "failed to get %s key from store (it's in %s)", role, filepath.Join(tempBaseDir, notary.PrivDir)) - pemPrivKey, err := utils.KeyToPEM(privKey, role, gun) + pemPrivKey, err := utils.ConvertPrivateKeyToPKCS8(privKey, role, gun, "") require.NoError(t, err, "failed to convert key to PEM") require.Equal(t, testData, pemPrivKey) } @@ -274,6 +248,9 @@ EMl3eFOJXjIch/wIesRSN+2dGOsl7neercjMh1i9RvpCwHDx/E0= // TestGetLegacyKey ensures we can still load keys where the role // is stored as part of the filename (i.e. _.key func TestGetLegacyKey(t *testing.T) { + if notary.FIPSEnabled() { + t.Skip("skip backward compatibility test in FIPS mode") + } testData := []byte(`-----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEAyUIXjsrWRrvPa4Bzp3VJ6uOUGPay2fUpSV8XzNxZxIG/Opdr +k3EQi1im6WOqF3Y5AS1UjYRxNuRN+cAZeo3uS1pOTuoSupBXuchVw8s4hZJ5vXn diff --git a/tuf/utils/pkcs8.go b/tuf/utils/pkcs8.go new file mode 100644 index 000000000..b0f11c427 --- /dev/null +++ b/tuf/utils/pkcs8.go @@ -0,0 +1,340 @@ +// Package utils contains tuf related utility functions however this file is hard +// forked from https://github.com/youmark/pkcs8 package. It has been further modified +// based on the requirements of Notary. For converting keys into PKCS#8 format, +// original package expected *crypto.PrivateKey interface, which then type inferred +// to either *rsa.PrivateKey or *ecdsa.PrivateKey depending on the need and later +// converted to ASN.1 DER encoded form, this whole process was superfluous here as +// keys are already being kept in ASN.1 DER format wrapped in data.PrivateKey +// structure. With these changes, package has became tightly coupled with notary as +// most of the method signatures have been updated. Moreover support for ED25519 +// keys has been added as well. License for original package is following: +// +// The MIT License (MIT) +// +// Copyright (c) 2014 youmark +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +package utils + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "crypto/sha1" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "errors" + "fmt" + + "golang.org/x/crypto/pbkdf2" + + "github.com/docker/notary/tuf/data" +) + +// Copy from crypto/x509 +var ( + oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} + oidPublicKeyDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1} + oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1} + // crypto/x509 doesn't have support for ED25519 + // http://www.oid-info.com/get/1.3.6.1.4.1.11591.15.1 + oidPublicKeyED25519 = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11591, 15, 1} +) + +// Copy from crypto/x509 +var ( + oidNamedCurveP224 = asn1.ObjectIdentifier{1, 3, 132, 0, 33} + oidNamedCurveP256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7} + oidNamedCurveP384 = asn1.ObjectIdentifier{1, 3, 132, 0, 34} + oidNamedCurveP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35} +) + +// Copy from crypto/x509 +func oidFromNamedCurve(curve elliptic.Curve) (asn1.ObjectIdentifier, bool) { + switch curve { + case elliptic.P224(): + return oidNamedCurveP224, true + case elliptic.P256(): + return oidNamedCurveP256, true + case elliptic.P384(): + return oidNamedCurveP384, true + case elliptic.P521(): + return oidNamedCurveP521, true + } + + return nil, false +} + +// Unecrypted PKCS8 +var ( + oidPKCS5PBKDF2 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 12} + oidPBES2 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 13} + oidAES256CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 42} +) + +type ecPrivateKey struct { + Version int + PrivateKey []byte + NamedCurveOID asn1.ObjectIdentifier `asn1:"optional,explicit,tag:0"` + PublicKey asn1.BitString `asn1:"optional,explicit,tag:1"` +} + +type privateKeyInfo struct { + Version int + PrivateKeyAlgorithm []asn1.ObjectIdentifier + PrivateKey []byte +} + +// Encrypted PKCS8 +type pbkdf2Params struct { + Salt []byte + IterationCount int +} + +type pbkdf2Algorithms struct { + IDPBKDF2 asn1.ObjectIdentifier + PBKDF2Params pbkdf2Params +} + +type pbkdf2Encs struct { + EncryAlgo asn1.ObjectIdentifier + IV []byte +} + +type pbes2Params struct { + KeyDerivationFunc pbkdf2Algorithms + EncryptionScheme pbkdf2Encs +} + +type pbes2Algorithms struct { + IDPBES2 asn1.ObjectIdentifier + PBES2Params pbes2Params +} + +type encryptedPrivateKeyInfo struct { + EncryptionAlgorithm pbes2Algorithms + EncryptedData []byte +} + +// pkcs8 reflects an ASN.1, PKCS#8 PrivateKey. +// copied from https://github.com/golang/go/blob/964639cc338db650ccadeafb7424bc8ebb2c0f6c/src/crypto/x509/pkcs8.go#L17 +type pkcs8 struct { + Version int + Algo pkix.AlgorithmIdentifier + PrivateKey []byte +} + +func parsePKCS8ToTufKey(der []byte) (data.PrivateKey, error) { + var key pkcs8 + + if _, err := asn1.Unmarshal(der, &key); err != nil { + if _, ok := err.(asn1.StructuralError); ok { + return nil, errors.New("could not decrypt private key") + } + return nil, err + } + + if key.Algo.Algorithm.Equal(oidPublicKeyED25519) { + tufED25519PrivateKey, err := ED25519ToPrivateKey(key.PrivateKey) + if err != nil { + return nil, fmt.Errorf("could not convert ed25519.PrivateKey to data.PrivateKey: %v", err) + } + + return tufED25519PrivateKey, nil + } + + privKey, err := x509.ParsePKCS8PrivateKey(der) + if err != nil { + return nil, err + } + + switch priv := privKey.(type) { + case *rsa.PrivateKey: + tufRSAPrivateKey, err := RSAToPrivateKey(priv) + if err != nil { + return nil, fmt.Errorf("could not convert rsa.PrivateKey to data.PrivateKey: %v", err) + } + + return tufRSAPrivateKey, nil + case *ecdsa.PrivateKey: + tufECDSAPrivateKey, err := ECDSAToPrivateKey(priv) + if err != nil { + return nil, fmt.Errorf("could not convert ecdsa.PrivateKey to data.PrivateKey: %v", err) + } + + return tufECDSAPrivateKey, nil + } + + return nil, errors.New("unsupported key type") +} + +// ParsePKCS8ToTufKey requires PKCS#8 key in DER format and returns data.PrivateKey +// Password should be provided in case of Encrypted PKCS#8 key, else it should be nil. +func ParsePKCS8ToTufKey(der []byte, password []byte) (data.PrivateKey, error) { + if password == nil { + return parsePKCS8ToTufKey(der) + } + + var privKey encryptedPrivateKeyInfo + if _, err := asn1.Unmarshal(der, &privKey); err != nil { + return nil, errors.New("pkcs8: only PKCS #5 v2.0 supported") + } + + if !privKey.EncryptionAlgorithm.IDPBES2.Equal(oidPBES2) { + return nil, errors.New("pkcs8: only PBES2 supported") + } + + if !privKey.EncryptionAlgorithm.PBES2Params.KeyDerivationFunc.IDPBKDF2.Equal(oidPKCS5PBKDF2) { + return nil, errors.New("pkcs8: only PBKDF2 supported") + } + + encParam := privKey.EncryptionAlgorithm.PBES2Params.EncryptionScheme + kdfParam := privKey.EncryptionAlgorithm.PBES2Params.KeyDerivationFunc.PBKDF2Params + + switch { + case encParam.EncryAlgo.Equal(oidAES256CBC): + iv := encParam.IV + salt := kdfParam.Salt + iter := kdfParam.IterationCount + + encryptedKey := privKey.EncryptedData + symkey := pbkdf2.Key(password, salt, iter, 32, sha1.New) + block, err := aes.NewCipher(symkey) + if err != nil { + return nil, err + } + mode := cipher.NewCBCDecrypter(block, iv) + mode.CryptBlocks(encryptedKey, encryptedKey) + + key, err := parsePKCS8ToTufKey(encryptedKey) + if err != nil { + return nil, errors.New("pkcs8: incorrect password") + } + + return key, nil + default: + return nil, errors.New("pkcs8: only AES-256-CBC supported") + } + +} + +func convertTUFKeyToPKCS8(priv data.PrivateKey) ([]byte, error) { + var pkey privateKeyInfo + + switch priv.Algorithm() { + case data.RSAKey, data.RSAx509Key: + // Per RFC5958, if publicKey is present, then version is set to v2(1) else version is set to v1(0). + // But openssl set to v1 even publicKey is present + pkey.Version = 0 + pkey.PrivateKeyAlgorithm = make([]asn1.ObjectIdentifier, 1) + pkey.PrivateKeyAlgorithm[0] = oidPublicKeyRSA + pkey.PrivateKey = priv.Private() + case data.ECDSAKey, data.ECDSAx509Key: + // To extract Curve value, parsing ECDSA key to *ecdsa.PrivateKey + eckey, err := x509.ParseECPrivateKey(priv.Private()) + if err != nil { + return nil, err + } + + oidNamedCurve, ok := oidFromNamedCurve(eckey.Curve) + if !ok { + return nil, errors.New("pkcs8: unknown elliptic curve") + } + + // Per RFC5958, if publicKey is present, then version is set to v2(1) else version is set to v1(0). + // But openssl set to v1 even publicKey is present + pkey.Version = 1 + pkey.PrivateKeyAlgorithm = make([]asn1.ObjectIdentifier, 2) + pkey.PrivateKeyAlgorithm[0] = oidPublicKeyECDSA + pkey.PrivateKeyAlgorithm[1] = oidNamedCurve + pkey.PrivateKey = priv.Private() + case data.ED25519Key: + pkey.Version = 0 + pkey.PrivateKeyAlgorithm = make([]asn1.ObjectIdentifier, 1) + pkey.PrivateKeyAlgorithm[0] = oidPublicKeyED25519 + pkey.PrivateKey = priv.Private() + default: + return nil, fmt.Errorf("algorithm %s not supported", priv.Algorithm()) + } + + return asn1.Marshal(pkey) +} + +func convertTUFKeyToPKCS8Encrypted(priv data.PrivateKey, password []byte) ([]byte, error) { + // Convert private key into PKCS8 format + pkey, err := convertTUFKeyToPKCS8(priv) + if err != nil { + return nil, err + } + + // Calculate key from password based on PKCS5 algorithm + // Use 8 byte salt, 16 byte IV, and 2048 iteration + iter := 2048 + salt := make([]byte, 8) + iv := make([]byte, 16) + _, err = rand.Reader.Read(salt) + if err != nil { + return nil, err + } + + _, err = rand.Reader.Read(iv) + if err != nil { + return nil, err + } + + key := pbkdf2.Key(password, salt, iter, 32, sha1.New) + + // Use AES256-CBC mode, pad plaintext with PKCS5 padding scheme + padding := aes.BlockSize - len(pkey)%aes.BlockSize + if padding > 0 { + n := len(pkey) + pkey = append(pkey, make([]byte, padding)...) + for i := 0; i < padding; i++ { + pkey[n+i] = byte(padding) + } + } + + encryptedKey := make([]byte, len(pkey)) + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + mode := cipher.NewCBCEncrypter(block, iv) + mode.CryptBlocks(encryptedKey, pkey) + + pbkdf2algo := pbkdf2Algorithms{oidPKCS5PBKDF2, pbkdf2Params{salt, iter}} + pbkdf2encs := pbkdf2Encs{oidAES256CBC, iv} + pbes2algo := pbes2Algorithms{oidPBES2, pbes2Params{pbkdf2algo, pbkdf2encs}} + + encryptedPkey := encryptedPrivateKeyInfo{pbes2algo, encryptedKey} + return asn1.Marshal(encryptedPkey) +} + +// ConvertTUFKeyToPKCS8 converts a private key (data.Private) to PKCS#8 and returns in DER format +// if password is not nil, it would convert the Private Key to Encrypted PKCS#8. +func ConvertTUFKeyToPKCS8(priv data.PrivateKey, password []byte) ([]byte, error) { + if password == nil { + return convertTUFKeyToPKCS8(priv) + } + return convertTUFKeyToPKCS8Encrypted(priv, password) +} diff --git a/tuf/utils/pkcs8_test.go b/tuf/utils/pkcs8_test.go new file mode 100644 index 000000000..82f791921 --- /dev/null +++ b/tuf/utils/pkcs8_test.go @@ -0,0 +1,317 @@ +package utils + +import ( + "crypto/rand" + "crypto/x509" + "encoding/pem" + "testing" + + "github.com/docker/notary/tuf/data" + "github.com/stretchr/testify/require" +) + +func TestConvertTUFKeyToPKCS8(t *testing.T) { + keys := []data.PrivateKey{} + + rsaKey, err := GenerateRSAKey(rand.Reader, 2048) + require.NoError(t, err) + keys = append(keys, rsaKey) + + ecKey, err := GenerateECDSAKey(rand.Reader) + require.NoError(t, err) + keys = append(keys, ecKey) + + edKey, err := GenerateED25519Key(rand.Reader) + require.NoError(t, err) + keys = append(keys, edKey) + + for _, k := range keys { + testConvertKeyToPKCS8(t, k, nil) + testConvertKeyToPKCS8(t, k, []byte("ponies")) + } +} + +func testConvertKeyToPKCS8(t *testing.T, privKey data.PrivateKey, password []byte) { + der, err := ConvertTUFKeyToPKCS8(privKey, password) + require.NoError(t, err, "could not convert private key to pkcs8") + + key, err := ParsePKCS8ToTufKey(der, password) + require.NoError(t, err, "could not decrypt the newly created pkcs8 key") + require.EqualValues(t, key.Private(), privKey.Private(), "private key did not match") +} + +func TestParsePKCS8ToTufKey(t *testing.T) { + keys := []struct { + // algorithm (supports: rsa, ecdsa, ed25519) + Algorithm string + // unencrypted PKCS#8 key + Unencrypted string + // encrypted PKCS#8 key + Encrypted string + // password used to encrypt key + Password string + }{ + { + Algorithm: "rsa", + Unencrypted: `-----BEGIN PRIVATE KEY----- +MIIEvgIBADALBgkqhkiG9w0BAQEEggSqMIIEpgIBAAKCAQEA3vRQI7s20MF0Zc3f +ywttsw72OkRXuTT0/JQrSuoilzOSaoLKp7sYprIeIu9OeXqvBbwAxe3i1GViGwWM +8cH9QqD05XhMz0Crr9vu2zHaZFEI9mgTXcQxMQGntZ4xYV/rL/fddzj7+n1oKNvo +vS800NvPEMUkkgApdp5ES605V1q51tBpLEYJ82xb5vT8cVseFYfA4G+gVqLNfQla +sa0QsQT4YlVEDbbwT3/wuMG/m+wTx2p8urhD+69oQbORkpqkNiEzMNidOrvtD7qy +ab+cUNamYU0CKOFn/KhWuoZV7EVYnc+oevm7naYsenDq43Q5hGyacEuTjGtLnUG3 +2d8RewIDAQABAoIBAQDeLOBfewSY6u8vNAU7tVvP/6znS4uPiHJJ8O1jbgaiXkYd +1dBVbWCXXRAjCA5PiC45rKuokfJkbdNh0houIH5ck0D4GvWP4oY0bRqNXBShuw8P +XY9O9V9/0oJpvga/XnJkDsCnOiX/7FCLxvka7ZvYNfMWZx6WT4sCJZ0xPKHTpT09 +SzbhDOCLOsM4nWbs5xkXuEGPkD289z+NOmENdcKDHz0mgYAr7hKJI3oAt2ogTjSy +iQBLmxgudBUP5oJ1qY6/kYUCTYE3cjssY/mqfNcKtylQpTIUaUCw8BhOf3yJFA0G +SI6C7hp96cjEk2dRQxAtYhSZJPA2uN+D1/UIUeSBAoGBAO9VnVaTvkvc55upY9kU +KnZcztJwG2Hf9FRrJrgC2RIaj3KNEImUQyPgIVBXRzvdrvtvNJ6Tdb0cg6CBLJu7 +IeQjca2Lj4ACIzoSMF8ak6BP3cdB/fCc2eHchqBKPWgZ23dq3CrpedtR6TbWLcsw +MrYdpZzpZ2eFPeStYxVhTLExAoGBAO56tNX+Sp4N4cCeht6ttljLnGfAjeSBWv4z ++xIqRyEoXbhchoppNxfnX34CrKmQM8MHfEYHKo27u/SkhnMGyuDOtrd51+jhB0LX +COH3w6GI162HVTRJXND8nUtCPB9h/SpFspr8Nk1Y2FtcfwkqhVphzExFzKm7eOPu +eevlsKJrAoGBALuvhh1Y60iOycpWggjAObRsf3yjkbWlbPOuu8Rd52C9F3UbjrZ1 +YFmH8FgSubgG1qwyvy8EMLbG36pE4niVvbQs337bDQOzqXBmxywtqUt0llUmOUAx +oOPwjlqxHYq/jE4PrOyx/2+wwpTQTUUkXQBYK4Hrv718zdbA6gzgKsZhAoGBAMsn +QufNMZmFL9xb737Assbf5QRJd1bCj1Zfx7FIzMFFVtlYENDWIrW9R47cDmSAUGgC +923cavbEh7A3e8V/ctKhpeuU40YidIIPFyUQYNo57amI0R+yo1vw5roW2YrOedFK +AIWg901asyzZFeskCufcyiHrkBbDeo+JNtmrGJazAoGBAMOxKBm9HpFXB0h90+FA +6aQgL6FfF578QTW+s5UsZUJjKunYuGmXnYOb3iF6Kvvc/qmKDB1TInVtho1N5TcD +pLTbO3/JPtJyolvYXjnBI7qXjpPxJeicDbA919iDaEVtW1tQOQ9g7WBG0VorWUSr +oQSGi2o00tBJXEiEPmsJK2HL +-----END PRIVATE KEY----- +`, + Encrypted: `-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFHzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIl+qYwi/xfGACAggA +MB0GCWCGSAFlAwQBKgQQT0TeNa0Rap4ngC1Xo5lEKwSCBNDVrsI+c8fmlsPfi98Q +AwIaR62VaolrBjcGvczqFoGMcJs2gcmMOdXBxCQua2E7EAAxnGKUjjkRH4ZyDdaC +hBgEejJ04pXTuhoi9eyuDgi6tVFW+LGmRo0RRQDm5VrB5x1hUFn2EczBnxWoh/Fv +IiaIetdIwGdVwuXyorQzrIqkv7/y2HMuokOXQjWudjK1rNLEi5dce7hgengYGg23 +xUiPIPGghkegjguFX/Kkn2+V+RtKAZtjfO6x5gr35EnFZe5FR0zhHPrwwIHm+YCl +4KEJIB3fchgUr8xOIB/WI1YRsubf2mir8SGY1iyePZ3Ya44xian2HpbRPxnHV7xF +FE34p4dzbuWtWBdJCaLWu4sXvPVujf0gxLRRFwskvnMUOWj12ZZvwQtS+hbVcuTC +95J8douCiuM65QOd4uVUdNPQftq6w2F2+V+m9cFUbFuztsCywuDuu7BM6z8ir5JV +GYq5FK5WIWio+EYSUkGhvrwWxFAdwyjTPVKWS8xhyhYfV38q+MpoKT8rbDDi6SbP +XQmtnm26du1C2Esuhd7XsosUBzsT7LENyRQQ/ECtCgJzw/iDGnRPeJW/hGp+Jwot +sWE4MOXbOjHl0kU4RSHwYuZ7AIixeJp7BrOFVQjyUlciRtxK0Qd0dSfeyGT7MRsD +IxU6jOFALGkSeDQGIkVLRUzqVGEWz+rMy7WpxsPqCw6H7mGXPkVgK4G4WRFGlfJo +MpYgSLfnHbh/HL7R5iJSp0GSXd8b3EkOSQXuJM6wJOoXyvnz9UXMeyQcev3fRi7g +XJoj38g410f8+8SIYKUbA629qhbpFn9C3U0pGIqqOBWvkdVYJJf7fxew0eD4ByB1 +sZEwRd7iMrH+Iivq4luGK2DpVf0JcyhBOqMTK0PpnIvWFwDTleQ2reyOH4SgfRCm +q+tuECSCWvkmzCcSODwZM/h2A6GtmkdBYoFP0l1KEgInvTG3eniOVYtq4G5SDk1J +nW9JE7egnk56Hg6k9gYwfKwvBOO8l10SO9SEXkD+PXVPMHaaYNhCgdL+s/509TWt +hNvfgplT1A+Q7tMnNYK9D9ZmQvsVhmA2QPhFxJNydvaIggoc5uXDedIc5sX6lIBK +kKUwQaeNoM5JhnG9Dfy0UKoJSLvo43KWeJRkW5guuqlmUcr5blGDwXSXnqKofY+1 +XomqtbZf77QstIp0LXG3x2bkxJxERC6UOcZz3mOh46eh52WFOztJ2nKc6v64aiPs +x0QqEkE/7cTT3ntKuvXZliFEMb9sTy5DJ+tm3pOpSC6XaOAFkCjvn9JllF+hCeEh +2jtMBZmpcOKrQpU//q540IbIZtbBR4zrziyMAz1Nhdsx+ziFeRsUeX3cEscQmTgt +3RDskN6xY/y6GBQcJ9sglnZIeAoD3WKsAUHJsbDJ2Cev+HtUpb/Ki/1xnSbw6wpC +5bkcOcVwdPlJ7Cz3nzCYN50YADmZBhGJEi3G+bEH31UdtuCe4/qkNaKsRxitwjxg +JkHt3S7Yw/wvg0WmRfR6cNXUpzqfbldJMfFapKKVNIesrqhZL8JAHNbF+wKFTeSr +Bsy4+8LUtpF26yx4+mTHMEPylWc1s6stNONnMcOCxMHdolbA7isX9JDN6Zr/yAtN +By5Wk9hG6JdPUxl/YkhZUCdKxg== +-----END ENCRYPTED PRIVATE KEY----- +`, + Password: "ponies", + }, + { + Algorithm: "ecdsa", + Unencrypted: `-----BEGIN PRIVATE KEY----- +MHgCAQAwEAYHKoZIzj0CAQYFK4EEACEEYTBfAgEBBBxdqDSBsFWIAiQ99sRSQZrb +IFczI8UIRM7FD/SNoTwDOgAETbjLZYByEmU3oALoLIz4Xr814S8jMs3cAfJuywm/ +kLGZ7y/1i56SXpTOByu6LHXrRokEi4hWQAc= +-----END PRIVATE KEY----- +`, + Encrypted: `-----BEGIN ENCRYPTED PRIVATE KEY----- +MIHOMEkGCSqGSIb3DQEFDTA8MBsGCSqGSIb3DQEFDDAOBAjsfXVXuwOQnAICCAAw +HQYJYIZIAWUDBAEqBBB0O793rOzupOUavjLSiPmBBIGALJxsXCe8rLBfeviStIH0 +A+1jCXUqXNm8D4npyNui/JRi/CjYPqgcO/2ulP8ppUAeTnLVQdhpv5ZOemK5ibMc +ECaNuzo40snnpve4duZEufkI9hXrO6MAMRT+G5ep1rKyIKboIPkzYUAdezj5ggUu +p1Gc8HB7j2SYjQX0Ybvlr6k= +-----END ENCRYPTED PRIVATE KEY----- +`, + Password: "ponies", + }, + { + Algorithm: "ecdsa", + Unencrypted: `-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgiwt5YfD/xQdVwJZ0 +2TpiJDQQ8DRHYVeWzIscya52BvChRANCAAT58IHVQJwbo3/MS/dFjh+xM85gVydX +xY+wxYDkaougZDPIgvu3+bQZ4xYSAnCGX7UJIiLloKuuuvbmXQlnSGqw +-----END PRIVATE KEY----- +`, + Encrypted: `-----BEGIN ENCRYPTED PRIVATE KEY----- +MIHeMEkGCSqGSIb3DQEFDTA8MBsGCSqGSIb3DQEFDDAOBAhTsVpOdfLrSwICCAAw +HQYJYIZIAWUDBAEqBBDjOBeXbqjRH1FP6BkI6n3PBIGQgdYzQ7wOKaEp73WloPJl +966A0tiBCt2wy4LSueFjlh5NtF0o8odzg+zK9lHGD0MluWwM9LsDk5xtfcXCE6Ya +16PfcoAKE6l7VuGob4wDms/Y0G9DLhKXxErQOfzEolNZjN5RcZF9938ZPjQUDeIX +yYeijYPrkZWmdcrFPZUkKY5HQMIjXoULlUtlN9fFckLn +-----END ENCRYPTED PRIVATE KEY----- +`, + Password: "ponies", + }, + { + Algorithm: "ecdsa", + Unencrypted: `-----BEGIN PRIVATE KEY----- +MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDCnjVESo9F+BLL4ZSt1 +/ZU14MYlozCa7OyjdcdFjwMSajUZ4N0HVoBpJoeFh8DKaJ2hZANiAAQ4sTZRVUFU +p4IXBI9QEuwWh0Lsd/uUtZkpwXrjC4hpCQI3am7QC5Ct83VAtQ1WXBYg7EjIYNfi +CDbvJdq1y0IhdY138OQvsTaewiuYHUvRwjljxiSjpNEOB6AoD36FlqY= +-----END PRIVATE KEY----- +`, + Encrypted: `-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBDjBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIPvZXgw/gzOsCAggA +MB0GCWCGSAFlAwQBKgQQIEKz02wepEM1NmHfAHd76wSBwALvEtH7pz03/m6Z5Gkv +aafc7pfImJJzpLVIxGcNxrLkz1/WFoIpXHR8Bdde4dBEa8TYz91KvSNfnFjGE2xk +AiMSZyuObwGB0Vw1de8tDlpsVYftkZC4VrpRwUEksTUzYgHum/sqRlm7DmeXJq2t +540HZ9XhS2ZfT1bSqaCMX1s98KMpxDDHRDPX0SEBskyGqIWKLzLfYJnf07OlZ8r4 +/oByTKigO+k9U40jNeuYW1aZeM8wqdApa89K48jWxftfNQ== +-----END ENCRYPTED PRIVATE KEY----- +`, + Password: "ponies", + }, + { + Algorithm: "ecdsa", + Unencrypted: `-----BEGIN PRIVATE KEY----- +MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIB0dZtwbNAy6K2iJF0 +P9cTcwv2XnSCyeiIcOW/IG3I09pklXQNCw1igQdKSjZLZZRVS4OZMvuG774OPq9j +F7m/tkihgYkDgYYABADN4kHmO0/+mIHmIuC6id/lX04mZ9wZovU102l4VUdZA3e6 +tZWDMdS2D3oqwhud2xCoHNw2ShxspzUISd/srH1pPAA3L2r2eZ6axrEqz1unbdBy +q1SyrsbtvDEJsP8STxiK3RSL9r00gqwlK44lp6dYQU3zd6IzS/69ACj/nmfX+YE4 +AA== +-----END PRIVATE KEY----- +`, + Encrypted: `-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBTzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQI0ykvTci7+gkCAggA +MB0GCWCGSAFlAwQBKgQQdd7n4DwrOqViFfmLvN7ERASCAQCuGSdBA8+YCSPpVhcj +bYxIU0knqQPrlF5N3+BJMGVIz3468DVZFi9UtiiKRaHGfSxaUimqyQ0oUzXULEav +ZSp6abjxoBJZXPJWHu0f85s3DOjoks990a5o+J72gH/dH9yK/GgvR3MSXlkKoMut +Zqm3toQAF5RReU3E2wirnYEec8h4Zw5gy3FX63MuvX9mhlOtHTPhiZjoM2ogVQ3b +iH1BRu4nZF11wSZNxtWflLGMGZaQv3zBt4w7eu6AN25U5DiTy6ReWmsyB+kD/cZ5 +eR9Noh2UlGMDPqTTrv2xqjjo/ieTusS7aGVQ0d8VESsxaAmCYx/kDY3FLtrKvsb5 +kB1E +-----END ENCRYPTED PRIVATE KEY----- +`, + Password: "ponies", + }, + { + Algorithm: "ed25519", + Unencrypted: `-----BEGIN PRIVATE KEY----- +MHICAQAwCwYJKwYBBAHaRw8BBGDkASR4b08nd+A8txI3h+1hG+7EAIxE5cdbv3gt +rwib9ibygTpRt8XjscMv+vum4zFjI2pPZbhQn6lZlumHo7g35AEkeG9PJ3fgPLcS +N4ftYRvuxACMROXHW794La8Im/Y= +-----END PRIVATE KEY----- +`, + Encrypted: `-----BEGIN ENCRYPTED PRIVATE KEY----- +MIHOMEkGCSqGSIb3DQEFDTA8MBsGCSqGSIb3DQEFDDAOBAhdzEpEYPy6ugICCAAw +HQYJYIZIAWUDBAEqBBCBSlYxcs3xzXsdF3JXvyvuBIGA9EaQcxFFk6d6jQiTzACC +66TFviduVfuqCr+VmulBAQZiCLj3PCpKugQ5z0aJ9CPfCRus5II3qS+qOXjI3OuH +hmevc2qO2C9bpsDteibfi9/tJJ8vVHdd+w44rSbLdLFro+p9CTT1R/VQillIFT4N +lXM12s3lyKsXT8bUcibd0gM= +-----END ENCRYPTED PRIVATE KEY----- +`, + Password: "ponies", + }, + } + + for _, k := range keys { + testParsePKCS8ToTufKey(t, k.Algorithm, []byte(k.Unencrypted), []byte(k.Encrypted), []byte(k.Password)) + } +} + +func testParsePKCS8ToTufKey(t *testing.T, alg string, unencypted, encrypted, password []byte) { + block, _ := pem.Decode(unencypted) + require.NotNil(t, block.Bytes, "could not decode PEM block properly") + + unencryptedKey, err := ParsePKCS8ToTufKey(block.Bytes, nil) + require.NoError(t, err, "could not parse pkcs8 to tuf key") + require.Equal(t, alg, unencryptedKey.Algorithm()) + + block, _ = pem.Decode(encrypted) + require.NotNil(t, block.Bytes, "could not decode PEM block properly") + + encryptedKey, err := ParsePKCS8ToTufKey(block.Bytes, password) + require.NoError(t, err, "could not parse pkcs8 to tuf key") + require.Equal(t, alg, encryptedKey.Algorithm()) + require.EqualValues(t, unencryptedKey.Private(), encryptedKey.Private()) + + _, err = ParsePKCS8ToTufKey(block.Bytes, []byte("wrong password")) + require.Error(t, err, "could parse key even with wrong password") +} + +func TestPEMtoPEM(t *testing.T) { + testInputPKCS1 := []byte(`-----BEGIN RSA PRIVATE KEY----- +MIIEpgIBAAKCAQEA3vRQI7s20MF0Zc3fywttsw72OkRXuTT0/JQrSuoilzOSaoLK +p7sYprIeIu9OeXqvBbwAxe3i1GViGwWM8cH9QqD05XhMz0Crr9vu2zHaZFEI9mgT +XcQxMQGntZ4xYV/rL/fddzj7+n1oKNvovS800NvPEMUkkgApdp5ES605V1q51tBp +LEYJ82xb5vT8cVseFYfA4G+gVqLNfQlasa0QsQT4YlVEDbbwT3/wuMG/m+wTx2p8 +urhD+69oQbORkpqkNiEzMNidOrvtD7qyab+cUNamYU0CKOFn/KhWuoZV7EVYnc+o +evm7naYsenDq43Q5hGyacEuTjGtLnUG32d8RewIDAQABAoIBAQDeLOBfewSY6u8v +NAU7tVvP/6znS4uPiHJJ8O1jbgaiXkYd1dBVbWCXXRAjCA5PiC45rKuokfJkbdNh +0houIH5ck0D4GvWP4oY0bRqNXBShuw8PXY9O9V9/0oJpvga/XnJkDsCnOiX/7FCL +xvka7ZvYNfMWZx6WT4sCJZ0xPKHTpT09SzbhDOCLOsM4nWbs5xkXuEGPkD289z+N +OmENdcKDHz0mgYAr7hKJI3oAt2ogTjSyiQBLmxgudBUP5oJ1qY6/kYUCTYE3cjss +Y/mqfNcKtylQpTIUaUCw8BhOf3yJFA0GSI6C7hp96cjEk2dRQxAtYhSZJPA2uN+D +1/UIUeSBAoGBAO9VnVaTvkvc55upY9kUKnZcztJwG2Hf9FRrJrgC2RIaj3KNEImU +QyPgIVBXRzvdrvtvNJ6Tdb0cg6CBLJu7IeQjca2Lj4ACIzoSMF8ak6BP3cdB/fCc +2eHchqBKPWgZ23dq3CrpedtR6TbWLcswMrYdpZzpZ2eFPeStYxVhTLExAoGBAO56 +tNX+Sp4N4cCeht6ttljLnGfAjeSBWv4z+xIqRyEoXbhchoppNxfnX34CrKmQM8MH +fEYHKo27u/SkhnMGyuDOtrd51+jhB0LXCOH3w6GI162HVTRJXND8nUtCPB9h/SpF +spr8Nk1Y2FtcfwkqhVphzExFzKm7eOPueevlsKJrAoGBALuvhh1Y60iOycpWggjA +ObRsf3yjkbWlbPOuu8Rd52C9F3UbjrZ1YFmH8FgSubgG1qwyvy8EMLbG36pE4niV +vbQs337bDQOzqXBmxywtqUt0llUmOUAxoOPwjlqxHYq/jE4PrOyx/2+wwpTQTUUk +XQBYK4Hrv718zdbA6gzgKsZhAoGBAMsnQufNMZmFL9xb737Assbf5QRJd1bCj1Zf +x7FIzMFFVtlYENDWIrW9R47cDmSAUGgC923cavbEh7A3e8V/ctKhpeuU40YidIIP +FyUQYNo57amI0R+yo1vw5roW2YrOedFKAIWg901asyzZFeskCufcyiHrkBbDeo+J +NtmrGJazAoGBAMOxKBm9HpFXB0h90+FA6aQgL6FfF578QTW+s5UsZUJjKunYuGmX +nYOb3iF6Kvvc/qmKDB1TInVtho1N5TcDpLTbO3/JPtJyolvYXjnBI7qXjpPxJeic +DbA919iDaEVtW1tQOQ9g7WBG0VorWUSroQSGi2o00tBJXEiEPmsJK2HL +-----END RSA PRIVATE KEY----- +`) + + testOutputPKCS8 := []byte(`-----BEGIN PRIVATE KEY----- +MIIEvgIBADALBgkqhkiG9w0BAQEEggSqMIIEpgIBAAKCAQEA3vRQI7s20MF0Zc3f +ywttsw72OkRXuTT0/JQrSuoilzOSaoLKp7sYprIeIu9OeXqvBbwAxe3i1GViGwWM +8cH9QqD05XhMz0Crr9vu2zHaZFEI9mgTXcQxMQGntZ4xYV/rL/fddzj7+n1oKNvo +vS800NvPEMUkkgApdp5ES605V1q51tBpLEYJ82xb5vT8cVseFYfA4G+gVqLNfQla +sa0QsQT4YlVEDbbwT3/wuMG/m+wTx2p8urhD+69oQbORkpqkNiEzMNidOrvtD7qy +ab+cUNamYU0CKOFn/KhWuoZV7EVYnc+oevm7naYsenDq43Q5hGyacEuTjGtLnUG3 +2d8RewIDAQABAoIBAQDeLOBfewSY6u8vNAU7tVvP/6znS4uPiHJJ8O1jbgaiXkYd +1dBVbWCXXRAjCA5PiC45rKuokfJkbdNh0houIH5ck0D4GvWP4oY0bRqNXBShuw8P +XY9O9V9/0oJpvga/XnJkDsCnOiX/7FCLxvka7ZvYNfMWZx6WT4sCJZ0xPKHTpT09 +SzbhDOCLOsM4nWbs5xkXuEGPkD289z+NOmENdcKDHz0mgYAr7hKJI3oAt2ogTjSy +iQBLmxgudBUP5oJ1qY6/kYUCTYE3cjssY/mqfNcKtylQpTIUaUCw8BhOf3yJFA0G +SI6C7hp96cjEk2dRQxAtYhSZJPA2uN+D1/UIUeSBAoGBAO9VnVaTvkvc55upY9kU +KnZcztJwG2Hf9FRrJrgC2RIaj3KNEImUQyPgIVBXRzvdrvtvNJ6Tdb0cg6CBLJu7 +IeQjca2Lj4ACIzoSMF8ak6BP3cdB/fCc2eHchqBKPWgZ23dq3CrpedtR6TbWLcsw +MrYdpZzpZ2eFPeStYxVhTLExAoGBAO56tNX+Sp4N4cCeht6ttljLnGfAjeSBWv4z ++xIqRyEoXbhchoppNxfnX34CrKmQM8MHfEYHKo27u/SkhnMGyuDOtrd51+jhB0LX +COH3w6GI162HVTRJXND8nUtCPB9h/SpFspr8Nk1Y2FtcfwkqhVphzExFzKm7eOPu +eevlsKJrAoGBALuvhh1Y60iOycpWggjAObRsf3yjkbWlbPOuu8Rd52C9F3UbjrZ1 +YFmH8FgSubgG1qwyvy8EMLbG36pE4niVvbQs337bDQOzqXBmxywtqUt0llUmOUAx +oOPwjlqxHYq/jE4PrOyx/2+wwpTQTUUkXQBYK4Hrv718zdbA6gzgKsZhAoGBAMsn +QufNMZmFL9xb737Assbf5QRJd1bCj1Zfx7FIzMFFVtlYENDWIrW9R47cDmSAUGgC +923cavbEh7A3e8V/ctKhpeuU40YidIIPFyUQYNo57amI0R+yo1vw5roW2YrOedFK +AIWg901asyzZFeskCufcyiHrkBbDeo+JNtmrGJazAoGBAMOxKBm9HpFXB0h90+FA +6aQgL6FfF578QTW+s5UsZUJjKunYuGmXnYOb3iF6Kvvc/qmKDB1TInVtho1N5TcD +pLTbO3/JPtJyolvYXjnBI7qXjpPxJeicDbA919iDaEVtW1tQOQ9g7WBG0VorWUSr +oQSGi2o00tBJXEiEPmsJK2HL +-----END PRIVATE KEY----- +`) + + block, _ := pem.Decode(testInputPKCS1) + require.NotEmpty(t, block) + + rsaKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) + require.NoError(t, err) + + testPrivKey, err := RSAToPrivateKey(rsaKey) + require.NoError(t, err) + + der, err := ConvertTUFKeyToPKCS8(testPrivKey, nil) + require.NoError(t, err, "could not convert pkcs1 to pkcs8") + + testOutput := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: der}) + require.EqualValues(t, testOutputPKCS8, testOutput) +} diff --git a/tuf/utils/x509.go b/tuf/utils/x509.go index f2b3a02bb..3f7413f96 100644 --- a/tuf/utils/x509.go +++ b/tuf/utils/x509.go @@ -85,12 +85,9 @@ func X509PublicKeyID(certPubKey data.PublicKey) (string, error) { return key.ID(), nil } -// ParsePEMPrivateKey returns a data.PrivateKey from a PEM encoded private key. It -// only supports RSA (PKCS#1) and attempts to decrypt using the passphrase, if encrypted. -func ParsePEMPrivateKey(pemBytes []byte, passphrase string) (data.PrivateKey, error) { - block, _ := pem.Decode(pemBytes) - if block == nil { - return nil, errors.New("no valid private key found") +func parseLegacyPrivateKey(block *pem.Block, passphrase string) (data.PrivateKey, error) { + if notary.FIPSEnabled() { + return nil, fmt.Errorf("%s not supported in FIPS mode", block.Type) } var privKeyBytes []byte @@ -145,6 +142,28 @@ func ParsePEMPrivateKey(pemBytes []byte, passphrase string) (data.PrivateKey, er } } +// ParsePEMPrivateKey returns a data.PrivateKey from a PEM encoded private key. It +// supports PKCS#8 as well as RSA/ECDSA (PKCS#1) only in non-FIPS mode and +// attempts to decrypt using the passphrase, if encrypted. +func ParsePEMPrivateKey(pemBytes []byte, passphrase string) (data.PrivateKey, error) { + block, _ := pem.Decode(pemBytes) + if block == nil { + return nil, errors.New("no valid private key found") + } + + switch block.Type { + case "RSA PRIVATE KEY", "EC PRIVATE KEY", "ED25519 PRIVATE KEY": + return parseLegacyPrivateKey(block, passphrase) + case "ENCRYPTED PRIVATE KEY", "PRIVATE KEY": + if passphrase == "" { + return ParsePKCS8ToTufKey(block.Bytes, nil) + } + return ParsePKCS8ToTufKey(block.Bytes, []byte(passphrase)) + default: + return nil, fmt.Errorf("unsupported key type %q", block.Type) + } +} + // CertToPEM is a utility function returns a PEM encoded x509 Certificate func CertToPEM(cert *x509.Certificate) []byte { pemCert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}) @@ -431,75 +450,54 @@ func ED25519ToPrivateKey(privKeyBytes []byte) (data.PrivateKey, error) { return data.NewED25519PrivateKey(*pubKey, privKeyBytes) } -func blockType(k data.PrivateKey) (string, error) { - switch k.Algorithm() { - case data.RSAKey, data.RSAx509Key: - return "RSA PRIVATE KEY", nil - case data.ECDSAKey, data.ECDSAx509Key: - return "EC PRIVATE KEY", nil - case data.ED25519Key: - return "ED25519 PRIVATE KEY", nil - default: - return "", fmt.Errorf("algorithm %s not supported", k.Algorithm()) - } -} - -// KeyToPEM returns a PEM encoded key from a Private Key -func KeyToPEM(privKey data.PrivateKey, role data.RoleName, gun data.GUN) ([]byte, error) { - bt, err := blockType(privKey) - if err != nil { - return nil, err - } - - headers := map[string]string{} - if role != "" { - headers["role"] = role.String() - } - if gun != "" { - headers["gun"] = gun.String() +// ExtractPrivateKeyAttributes extracts role and gun values from private key bytes +func ExtractPrivateKeyAttributes(pemBytes []byte) (data.RoleName, data.GUN, error) { + block, _ := pem.Decode(pemBytes) + if block == nil { + return "", "", errors.New("PEM block is empty") } - block := &pem.Block{ - Type: bt, - Headers: headers, - Bytes: privKey.Private(), + switch block.Type { + case "RSA PRIVATE KEY", "EC PRIVATE KEY", "ED25519 PRIVATE KEY": + if notary.FIPSEnabled() { + return "", "", fmt.Errorf("%s not supported in FIPS mode", block.Type) + } + case "PRIVATE KEY", "ENCRYPTED PRIVATE KEY": + // do nothing for PKCS#8 keys + default: + return "", "", errors.New("unknown key format") } - - return pem.EncodeToMemory(block), nil + return data.RoleName(block.Headers["role"]), data.GUN(block.Headers["gun"]), nil } -// EncryptPrivateKey returns an encrypted PEM key given a Privatekey -// and a passphrase -func EncryptPrivateKey(key data.PrivateKey, role data.RoleName, gun data.GUN, passphrase string) ([]byte, error) { - bt, err := blockType(key) - if err != nil { - return nil, err - } +// ConvertPrivateKeyToPKCS8 converts a data.PrivateKey to PKCS#8 Format +func ConvertPrivateKeyToPKCS8(key data.PrivateKey, role data.RoleName, gun data.GUN, passphrase string) ([]byte, error) { + var ( + err error + der []byte + blockType = "PRIVATE KEY" + ) - password := []byte(passphrase) - cipherType := x509.PEMCipherAES256 - - encryptedPEMBlock, err := x509.EncryptPEMBlock(rand.Reader, - bt, - key.Private(), - password, - cipherType) - if err != nil { - return nil, err + if passphrase == "" { + der, err = ConvertTUFKeyToPKCS8(key, nil) + } else { + blockType = "ENCRYPTED PRIVATE KEY" + der, err = ConvertTUFKeyToPKCS8(key, []byte(passphrase)) } - - if encryptedPEMBlock.Headers == nil { - return nil, fmt.Errorf("unable to encrypt key - invalid PEM file produced") + if err != nil { + return nil, fmt.Errorf("unable to convert to PKCS8 key") } + headers := make(map[string]string) if role != "" { - encryptedPEMBlock.Headers["role"] = role.String() + headers["role"] = role.String() } + if gun != "" { - encryptedPEMBlock.Headers["gun"] = gun.String() + headers["gun"] = gun.String() } - return pem.EncodeToMemory(encryptedPEMBlock), nil + return pem.EncodeToMemory(&pem.Block{Bytes: der, Type: blockType, Headers: headers}), nil } // CertToKey transforms a single input certificate into its corresponding diff --git a/tuf/utils/x509_test.go b/tuf/utils/x509_test.go index 7d02a3910..246b0c1bd 100644 --- a/tuf/utils/x509_test.go +++ b/tuf/utils/x509_test.go @@ -6,11 +6,15 @@ import ( "crypto/rand" "crypto/rsa" "crypto/x509" + "encoding/pem" + "fmt" "io/ioutil" + "os" "strings" "testing" "time" + "github.com/docker/notary" "github.com/docker/notary/tuf/data" "github.com/stretchr/testify/require" ) @@ -87,28 +91,41 @@ func TestKeyOperations(t *testing.T) { require.NoError(t, err) // Encode our ED private key - edPEM, err := KeyToPEM(edKey, data.CanonicalRootRole, "") + edPEM, err := ConvertPrivateKeyToPKCS8(edKey, data.CanonicalRootRole, "", "") require.NoError(t, err) // Encode our EC private key - ecPEM, err := KeyToPEM(ecKey, data.CanonicalRootRole, "") + ecPEM, err := ConvertPrivateKeyToPKCS8(ecKey, data.CanonicalRootRole, "", "") require.NoError(t, err) // Encode our RSA private key - rsaPEM, err := KeyToPEM(rsaKey, data.CanonicalRootRole, "") + rsaPEM, err := ConvertPrivateKeyToPKCS8(rsaKey, data.CanonicalRootRole, "", "") require.NoError(t, err) // Check to see if ED key it is encoded stringEncodedEDKey := string(edPEM) - require.True(t, strings.Contains(stringEncodedEDKey, "-----BEGIN ED25519 PRIVATE KEY-----")) + require.True(t, strings.Contains(stringEncodedEDKey, "-----BEGIN PRIVATE KEY-----")) + + // Check to see the ED key type + testKeyBlockType(t, edPEM, nil, "ed25519") // Check to see if EC key it is encoded stringEncodedECKey := string(ecPEM) - require.True(t, strings.Contains(stringEncodedECKey, "-----BEGIN EC PRIVATE KEY-----")) + require.True(t, strings.Contains(stringEncodedECKey, "-----BEGIN PRIVATE KEY-----")) + + // Check to see the EC key type + testKeyBlockType(t, ecPEM, nil, "ecdsa") // Check to see if RSA key it is encoded stringEncodedRSAKey := string(rsaPEM) - require.True(t, strings.Contains(stringEncodedRSAKey, "-----BEGIN RSA PRIVATE KEY-----")) + require.True(t, strings.Contains(stringEncodedRSAKey, "-----BEGIN PRIVATE KEY-----")) + + // Check to see the RSA key type + testKeyBlockType(t, rsaPEM, nil, "rsa") + + // Try to decode garbage bytes + _, err = ParsePEMPrivateKey([]byte("Knock knock; it's Bob."), "") + require.Error(t, err) // Decode our ED Key decodedEDKey, err := ParsePEMPrivateKey(edPEM, "") @@ -126,34 +143,37 @@ func TestKeyOperations(t *testing.T) { require.Equal(t, rsaKey.Private(), decodedRSAKey.Private()) // Encrypt our ED Key - encryptedEDKey, err := EncryptPrivateKey(edKey, data.CanonicalRootRole, "", "ponies") + encryptedEDKey, err := ConvertPrivateKeyToPKCS8(edKey, data.CanonicalRootRole, "", "ponies") require.NoError(t, err) // Encrypt our EC Key - encryptedECKey, err := EncryptPrivateKey(ecKey, data.CanonicalRootRole, "", "ponies") + encryptedECKey, err := ConvertPrivateKeyToPKCS8(ecKey, data.CanonicalRootRole, "", "ponies") require.NoError(t, err) // Encrypt our RSA Key - encryptedRSAKey, err := EncryptPrivateKey(rsaKey, data.CanonicalRootRole, "", "ponies") + encryptedRSAKey, err := ConvertPrivateKeyToPKCS8(rsaKey, data.CanonicalRootRole, "", "ponies") require.NoError(t, err) // Check to see if ED key it is encrypted stringEncryptedEDKey := string(encryptedEDKey) - require.True(t, strings.Contains(stringEncryptedEDKey, "-----BEGIN ED25519 PRIVATE KEY-----")) - require.True(t, strings.Contains(stringEncryptedEDKey, "Proc-Type: 4,ENCRYPTED")) - require.True(t, strings.Contains(stringEncryptedEDKey, "role: root")) + require.True(t, strings.Contains(stringEncryptedEDKey, "-----BEGIN ENCRYPTED PRIVATE KEY-----")) + role, _, err := ExtractPrivateKeyAttributes(encryptedEDKey) + require.NoError(t, err) + require.EqualValues(t, "root", role) // Check to see if EC key it is encrypted stringEncryptedECKey := string(encryptedECKey) - require.True(t, strings.Contains(stringEncryptedECKey, "-----BEGIN EC PRIVATE KEY-----")) - require.True(t, strings.Contains(stringEncryptedECKey, "Proc-Type: 4,ENCRYPTED")) - require.True(t, strings.Contains(stringEncryptedECKey, "role: root")) + require.True(t, strings.Contains(stringEncryptedECKey, "-----BEGIN ENCRYPTED PRIVATE KEY-----")) + role, _, err = ExtractPrivateKeyAttributes(encryptedECKey) + require.NoError(t, err) + require.EqualValues(t, "root", role) // Check to see if RSA key it is encrypted stringEncryptedRSAKey := string(encryptedRSAKey) - require.True(t, strings.Contains(stringEncryptedRSAKey, "-----BEGIN RSA PRIVATE KEY-----")) - require.True(t, strings.Contains(stringEncryptedRSAKey, "Proc-Type: 4,ENCRYPTED")) - require.True(t, strings.Contains(stringEncryptedRSAKey, "role: root")) + require.True(t, strings.Contains(stringEncryptedRSAKey, "-----BEGIN ENCRYPTED PRIVATE KEY-----")) + role, _, err = ExtractPrivateKeyAttributes(encryptedRSAKey) + require.NoError(t, err) + require.EqualValues(t, "root", role) // Decrypt our ED Key decryptedEDKey, err := ParsePEMPrivateKey(encryptedEDKey, "ponies") @@ -172,17 +192,29 @@ func TestKeyOperations(t *testing.T) { // quick test that gun headers are being added appropriately // Encrypt our RSA Key, one type of key should be enough since headers are treated the same - testGunKey, err := EncryptPrivateKey(rsaKey, data.CanonicalRootRole, "ilove", "ponies") + testGunKey, err := ConvertPrivateKeyToPKCS8(rsaKey, data.CanonicalRootRole, "ilove", "ponies") + require.NoError(t, err) + + testNoGunKey, err := ConvertPrivateKeyToPKCS8(rsaKey, data.CanonicalRootRole, "", "ponies") require.NoError(t, err) - testNoGunKey, err := EncryptPrivateKey(rsaKey, data.CanonicalRootRole, "", "ponies") + _, gun, err := ExtractPrivateKeyAttributes(testGunKey) require.NoError(t, err) + require.EqualValues(t, "ilove", gun) - stringTestGunKey := string(testGunKey) - require.True(t, strings.Contains(stringTestGunKey, "gun: ilove")) + _, gun, err = ExtractPrivateKeyAttributes(testNoGunKey) + require.NoError(t, err) + require.EqualValues(t, "", gun) +} + +func testKeyBlockType(t *testing.T, b, password []byte, expectedKeyType string) { + block, _ := pem.Decode(b) + require.NotNil(t, block) - stringTestNoGunKey := string(testNoGunKey) - require.False(t, strings.Contains(stringTestNoGunKey, "gun:")) + privKey, err := ParsePKCS8ToTufKey(block.Bytes, password) + require.NoError(t, err, "unable to parse to pkcs8") + + require.Equal(t, expectedKeyType, privKey.Algorithm(), "key type did not match") } // X509PublickeyID returns the public key ID of a RSA X509 key rather than the @@ -239,6 +271,310 @@ func TestECDSAX509PublickeyID(t *testing.T) { require.Equal(t, tufPrivKey.ID(), tufID) } +func preserveEnv(name string) func() { + if env, has := os.LookupEnv(name); has { + os.Unsetenv(name) + return func() { + os.Setenv(name, env) + } + } + + return func() {} +} + +func TestExtractPrivateKeyAttributes(t *testing.T) { + testExtractPrivateKeyAttributes(t) + testExtractPrivateKeyAttributesWithFIPS(t) +} + +func testExtractPrivateKeyAttributes(t *testing.T) { + defer preserveEnv(notary.FIPSEnvVar)() + + err := os.Unsetenv(notary.FIPSEnvVar) + require.NoError(t, err) + + testPKCS1PEM1 := getPKCS1KeyWithRole(t, "unicorn", "rainbow") + testPKCS1PEM2 := getPKCS1KeyWithRole(t, "docker", "") + testPKCS8PEM1 := getPKCS8KeyWithRole(t, "fat", "panda") + testPKCS8PEM2 := getPKCS8KeyWithRole(t, "dagger", "") + + // Try garbage bytes + _, _, err = ExtractPrivateKeyAttributes([]byte("Knock knock; it's Bob.")) + require.Error(t, err) + + // PKCS#8 + role, gun, err := ExtractPrivateKeyAttributes(testPKCS8PEM1) + require.NoError(t, err) + require.EqualValues(t, data.RoleName("fat"), role) + require.EqualValues(t, data.GUN("panda"), gun) + + role, gun, err = ExtractPrivateKeyAttributes(testPKCS8PEM2) + require.NoError(t, err) + require.EqualValues(t, data.RoleName("dagger"), role) + require.EqualValues(t, data.GUN(""), gun) + + // PKCS#1 + role, gun, err = ExtractPrivateKeyAttributes(testPKCS1PEM1) + require.NoError(t, err) + require.EqualValues(t, data.RoleName("unicorn"), role) + require.EqualValues(t, data.GUN("rainbow"), gun) + + role, gun, err = ExtractPrivateKeyAttributes(testPKCS1PEM2) + require.NoError(t, err) + require.EqualValues(t, data.RoleName("docker"), role) + require.EqualValues(t, data.GUN(""), gun) +} + +func testExtractPrivateKeyAttributesWithFIPS(t *testing.T) { + defer preserveEnv(notary.FIPSEnvVar)() + + err := os.Setenv(notary.FIPSEnvVar, "1") + require.NoError(t, err) + + testPKCS1PEM1 := getPKCS1KeyWithRole(t, "unicorn", "rainbow") + testPKCS1PEM2 := getPKCS1KeyWithRole(t, "docker", "") + + // PKCS#1 + _, _, err = ExtractPrivateKeyAttributes(testPKCS1PEM1) + require.Error(t, err) + _, _, err = ExtractPrivateKeyAttributes(testPKCS1PEM2) + require.Error(t, err) + + testPKCS8PEM1 := getPKCS8KeyWithRole(t, "fat", "panda") + testPKCS8PEM2 := getPKCS8KeyWithRole(t, "dagger", "") + + // Try garbage bytes + _, _, err = ExtractPrivateKeyAttributes([]byte("Knock knock; it's Bob.")) + require.Error(t, err) + + // PKCS#8 + role, gun, err := ExtractPrivateKeyAttributes(testPKCS8PEM1) + require.NoError(t, err) + require.EqualValues(t, data.RoleName("fat"), role) + require.EqualValues(t, data.GUN("panda"), gun) + + role, gun, err = ExtractPrivateKeyAttributes(testPKCS8PEM2) + require.NoError(t, err) + require.EqualValues(t, data.RoleName("dagger"), role) + require.EqualValues(t, data.GUN(""), gun) +} + +func TestParsePEMPrivateKeyLegacy(t *testing.T) { + keys := []string{ + `-----BEGIN ED25519 PRIVATE KEY----- +role: root + +cVlz659cVO/F6/EOGjuXms1RvX/PmSJalv341Un6N7qmcFuchu5zBcRBZI4nDFwn +dXJONWxS4OL7kgBTbEeYtnFZc+ufXFTvxevxDho7l5rNUb1/z5kiWpb9+NVJ+je6 +-----END ED25519 PRIVATE KEY----- +`, + `-----BEGIN EC PRIVATE KEY----- +role: root + +MHcCAQEEILV9cO28WyG1iMPUFXP++AtWDBJv5hQeusiUHL4EeUzRoAoGCCqGSM49 +AwEHoUQDQgAEylKk0nv1In9WNEhTI39MHZhgC8OCBE+Qp/7Fbz65bzSkeFYRIz7x +122yOUaphJOboK52o6HDbs4QcIkJ0yl/NQ== +-----END EC PRIVATE KEY----- +`, + `-----BEGIN RSA PRIVATE KEY----- +role: root + +MIIEpAIBAAKCAQEAqDpGSq+s8LyhCI8KVscVFfDxMlJdZ8QthwG4/GvKXXNpjyrn +49Tl5Hn9KUAyNVpzYNU6+KbQcprFiIUAYljlC4MCHLKsSTYHlRVnPszahgzMnkBJ +wRhgjmvG9smpzFd/0typ1ToRwIJuLPmnT1xC15F7x3WdOx55lJ+WRG5tkhLflKRC +Uw8JlDEBbMbhOO1E3IS2OeCReLgMao8yalltsN7+4GghXmVCLc0Iewvkx6DMeNRD +FEFRQJpHzbU5DWU3I8hf6yDyJ7+gfSYnyYPX6mgh3IvJHqRNYdfMZHFe+/pEM+DL +dOxUpIMgCp57DkwubgoHaZuWt6ix8IB6VJi1dQIDAQABAoIBAGiQ0GBcEgMRPIc1 +YhfOZyzcNxAwYh69sg7Y40MDPSYZNuPmp3zWOI9rxBB/9rVzI4RtBdrI1Yhm66GQ +Ck0XNEeThxyPcseO7eedBi/i5XGtQwKasz1zCZF9LI75irGZMbq/rlD7Z01hxVnv +VC/gCSw1Ids5ICI/LxNSnvSqLzE7x11n6beXoummFoncvQDoFrjM3PrFRxVpLppJ +g3IWGwvQIAhTNA6ahUItuPnZhncARTCaYsfTNbX/zjbWhXIcL6MGJ2dVVJPLwVpY +3pvvbCR0oJKIJrJcXqD+ax/xhmYNAU4/LcNI9tirGHRxx+uJ6/zW4f+tRF9zXCyU +G6m/oAECgYEA3qZ86b/YeilO9Oa+7HaClHWCYMXNhAGYviNHqRvRSq849QyWwCRk +qvogdBYq+5KfX87ASIZFWmgM51QkmloF0DRurit3YRR0PuTuIZr11M+kCB7+HWrd +DC+3CzYpY6hQU0fsNLBl/x8std4RhjXa6oApsJYjAyr8adY2Qg5q0DUCgYEAwWzz +nlwacNefFN6YTXWQjGZiHxvHBnXQP5OLuTI3nUNWh4/tJ3/ktsuWFkHFW7rlS2B6 +avSbpycMCxD2mdHt+IlBfzAZAG1ik23SttFbfD4nnQN7NktjZwTmKI5tjCvs3HB6 +JO4CcxJ/VNw/WtHlJu2oPViAy8cLOm2k3TB9eEECgYBFAaTFbchSVGs8TCfwceqW +yLTX+XZw623DwHt9VjnPw+8LRBOVCbKJq2xTjmtT/WWX9CR0Vek40/br25BcpnoW +xaloIeCmHgjJVXrYv4ZhptlYCwMHaw+Hr2Iz/11knc4HgcsbqXBzWd4pn+IejqKC ++6XwLRg86x3AT7wRTRad4QKBgQCMs62Pn54YQbFV5ApUJlYM25k62eDwIRlodfLo +t8/e1RIHsLmpxw3frr6x2AwxiwWqzDagwOjNMck/74oDIMODzIxZcept9iQD7Jqg +JDDxcuEsBVFGkJZxZQ3rqJelpHo7bJJddMlRXb5EQ6bOcOrJY43DejLOiS7wxLtt +rw1GQQKBgQCKOduk0ugZe/t/BSkqf3/vlEk4FjwErPMTpLnsVPN13QA5//XHahkm +bsCmy8401/anZGa1s6m58UswCWNhJlCtfozN/rtkgjWZGOtlc3at0MYDdObBynVg +PBV11bfmoHzDVeeuz1ztFUb3WjR7xlQe09izY3o3N6yZlTFIsqawIg== +-----END RSA PRIVATE KEY----- +`, + } + + for _, key := range keys { + testParsePEMPrivateKeyLegacy(t, []byte(key)) + testParsePEMPrivateKeyLegacyWithFIPS(t, []byte(key)) + } +} + +func testParsePEMPrivateKeyLegacy(t *testing.T, raw []byte) { + defer preserveEnv(notary.FIPSEnvVar)() + + err := os.Unsetenv(notary.FIPSEnvVar) + require.NoError(t, err) + + key, err := ParsePEMPrivateKey(raw, "") + require.NoError(t, err) + require.NotNil(t, key.Public()) + require.NotNil(t, key.Private()) +} + +func testParsePEMPrivateKeyLegacyWithFIPS(t *testing.T, raw []byte) { + defer preserveEnv(notary.FIPSEnvVar)() + + err := os.Setenv(notary.FIPSEnvVar, "1") + require.NoError(t, err) + + // No legacy key must be accepted in FIPS mode + _, err = ParsePEMPrivateKey(raw, "") + require.Error(t, err) +} + +func getPKCS1KeyWithRole(t *testing.T, role data.RoleName, gun data.GUN) []byte { + var testPEM []byte + if gun == "" { + testPEM = []byte(fmt.Sprintf(`-----BEGIN RSA PRIVATE KEY----- +role: %s +MIIEogIBAAKCAQEAyUIXjsrWRrvPa4Bzp3VJ6uOUGPay2fUpSV8XzNxZxIG/Opdr ++k3EQi1im6WOqF3Y5AS1UjYRxNuRN+cAZeo3uS1pOTuoSupBXuchVw8s4hZJ5vXn +TRmGb+xY7tZ1ZVgPfAZDib9sRSUsL/gC+aSyprAjG/YBdbF06qKbfOfsoCEYW1OQ +82JqHzQH514RFYPTnEGpvfxWaqmFQLmv0uMxV/cAYvqtrGkXuP0+a8PknlD2obw5 +0rHE56Su1c3Q42S7L51K38tpbgWOSRcTfDUWEj5v9wokkNQvyKBwbS996s4EJaZd +7r6M0h1pHnuRxcSaZLYRwgOe1VNGg2VfWzgd5QIDAQABAoIBAF9LGwpygmj1jm3R +YXGd+ITugvYbAW5wRb9G9mb6wspnwNsGTYsz/UR0ZudZyaVw4jx8+jnV/i3e5PC6 +QRcAgqf8l4EQ/UuThaZg/AlT1yWp9g4UyxNXja87EpTsGKQGwTYxZRM4/xPyWOzR +mt8Hm8uPROB9aA2JG9npaoQG8KSUj25G2Qot3ukw/IOtqwN/Sx1EqF0EfCH1K4KU +a5TrqlYDFmHbqT1zTRec/BTtVXNsg8xmF94U1HpWf3Lpg0BPYT7JiN2DPoLelRDy +a/A+a3ZMRNISL5wbq/jyALLOOyOkIqa+KEOeW3USuePd6RhDMzMm/0ocp5FCwYfo +k4DDeaECgYEA0eSMD1dPGo+u8UTD8i7ZsZCS5lmXLNuuAg5f5B/FGghD8ymPROIb +dnJL5QSbUpmBsYJ+nnO8RiLrICGBe7BehOitCKi/iiZKJO6edrfNKzhf4XlU0HFl +jAOMa975pHjeCoZ1cXJOEO9oW4SWTCyBDBSqH3/ZMgIOiIEk896lSmkCgYEA9Xf5 +Jqv3HtQVvjugV/axAh9aI8LMjlfFr9SK7iXpY53UdcylOSWKrrDok3UnrSEykjm7 +UL3eCU5jwtkVnEXesNn6DdYo3r43E6iAiph7IBkB5dh0yv3vhIXPgYqyTnpdz4pg +3yPGBHMPnJUBThg1qM7k6a2BKHWySxEgC1DTMB0CgYAGvdmF0J8Y0k6jLzs/9yNE +4cjmHzCM3016gW2xDRgumt9b2xTf+Ic7SbaIV5qJj6arxe49NqhwdESrFohrKaIP +kM2l/o2QaWRuRT/Pvl2Xqsrhmh0QSOQjGCYVfOb10nAHVIRHLY22W4o1jk+piLBo +a+1+74NRaOGAnu1J6/fRKQKBgAF180+dmlzemjqFlFCxsR/4G8s2r4zxTMXdF+6O +3zKuj8MbsqgCZy7e8qNeARxwpCJmoYy7dITNqJ5SOGSzrb2Trn9ClP+uVhmR2SH6 +AlGQlIhPn3JNzI0XVsLIloMNC13ezvDE/7qrDJ677EQQtNEKWiZh1/DrsmHr+irX +EkqpAoGAJWe8PC0XK2RE9VkbSPg9Ehr939mOLWiHGYTVWPttUcum/rTKu73/X/mj +WxnPWGtzM1pHWypSokW90SP4/xedMxludvBvmz+CTYkNJcBGCrJumy11qJhii9xp +EMl3eFOJXjIch/wIesRSN+2dGOsl7neercjMh1i9RvpCwHDx/E0= +-----END RSA PRIVATE KEY----- +`, role)) + } else { + testPEM = []byte(fmt.Sprintf(`-----BEGIN RSA PRIVATE KEY----- +gun: %s +role: %s +MIIEogIBAAKCAQEAyUIXjsrWRrvPa4Bzp3VJ6uOUGPay2fUpSV8XzNxZxIG/Opdr ++k3EQi1im6WOqF3Y5AS1UjYRxNuRN+cAZeo3uS1pOTuoSupBXuchVw8s4hZJ5vXn +TRmGb+xY7tZ1ZVgPfAZDib9sRSUsL/gC+aSyprAjG/YBdbF06qKbfOfsoCEYW1OQ +82JqHzQH514RFYPTnEGpvfxWaqmFQLmv0uMxV/cAYvqtrGkXuP0+a8PknlD2obw5 +0rHE56Su1c3Q42S7L51K38tpbgWOSRcTfDUWEj5v9wokkNQvyKBwbS996s4EJaZd +7r6M0h1pHnuRxcSaZLYRwgOe1VNGg2VfWzgd5QIDAQABAoIBAF9LGwpygmj1jm3R +YXGd+ITugvYbAW5wRb9G9mb6wspnwNsGTYsz/UR0ZudZyaVw4jx8+jnV/i3e5PC6 +QRcAgqf8l4EQ/UuThaZg/AlT1yWp9g4UyxNXja87EpTsGKQGwTYxZRM4/xPyWOzR +mt8Hm8uPROB9aA2JG9npaoQG8KSUj25G2Qot3ukw/IOtqwN/Sx1EqF0EfCH1K4KU +a5TrqlYDFmHbqT1zTRec/BTtVXNsg8xmF94U1HpWf3Lpg0BPYT7JiN2DPoLelRDy +a/A+a3ZMRNISL5wbq/jyALLOOyOkIqa+KEOeW3USuePd6RhDMzMm/0ocp5FCwYfo +k4DDeaECgYEA0eSMD1dPGo+u8UTD8i7ZsZCS5lmXLNuuAg5f5B/FGghD8ymPROIb +dnJL5QSbUpmBsYJ+nnO8RiLrICGBe7BehOitCKi/iiZKJO6edrfNKzhf4XlU0HFl +jAOMa975pHjeCoZ1cXJOEO9oW4SWTCyBDBSqH3/ZMgIOiIEk896lSmkCgYEA9Xf5 +Jqv3HtQVvjugV/axAh9aI8LMjlfFr9SK7iXpY53UdcylOSWKrrDok3UnrSEykjm7 +UL3eCU5jwtkVnEXesNn6DdYo3r43E6iAiph7IBkB5dh0yv3vhIXPgYqyTnpdz4pg +3yPGBHMPnJUBThg1qM7k6a2BKHWySxEgC1DTMB0CgYAGvdmF0J8Y0k6jLzs/9yNE +4cjmHzCM3016gW2xDRgumt9b2xTf+Ic7SbaIV5qJj6arxe49NqhwdESrFohrKaIP +kM2l/o2QaWRuRT/Pvl2Xqsrhmh0QSOQjGCYVfOb10nAHVIRHLY22W4o1jk+piLBo +a+1+74NRaOGAnu1J6/fRKQKBgAF180+dmlzemjqFlFCxsR/4G8s2r4zxTMXdF+6O +3zKuj8MbsqgCZy7e8qNeARxwpCJmoYy7dITNqJ5SOGSzrb2Trn9ClP+uVhmR2SH6 +AlGQlIhPn3JNzI0XVsLIloMNC13ezvDE/7qrDJ677EQQtNEKWiZh1/DrsmHr+irX +EkqpAoGAJWe8PC0XK2RE9VkbSPg9Ehr939mOLWiHGYTVWPttUcum/rTKu73/X/mj +WxnPWGtzM1pHWypSokW90SP4/xedMxludvBvmz+CTYkNJcBGCrJumy11qJhii9xp +EMl3eFOJXjIch/wIesRSN+2dGOsl7neercjMh1i9RvpCwHDx/E0= +-----END RSA PRIVATE KEY----- +`, gun, role)) + } + + return testPEM +} + +func getPKCS8KeyWithRole(t *testing.T, role data.RoleName, gun data.GUN) []byte { + var testPEM []byte + if gun == "" { + testPEM = []byte(fmt.Sprintf(`-----BEGIN PRIVATE KEY----- +role: %s +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCo23n5TrVazr9C +DniRin4uSxx9w4tDtp5WbvACE0iWw2T0l6GHPnKa6aBFmJ3GxRQtwveM+cQVub3A +KjIS7OdJpeQA5fOrpnC5dgv5l/DbmZ5SHrzAm9JgYmrw7Uj1dyeQ6jpPra4ChF7P +YOIcoeTp4eWOSJzztZQyftnBLVRgTIYXXX1kVZVfWVFsT2FQk7ei9Gw/UnslyVQd +HZxYa98SAmsoQ5YZb3I11Tk1LHsCS6Py9p6tL3vdyW9rJioqNu2RhO/WwhqiSttd +/xTanwJRlD5IWE32CU3II4UtaZZrYDeimRekVV7zqwgTBAzNsmatZhD1o6E9LZ9D +JFhKwwDXAgMBAAECggEAbqa0PV0IlqMYze6xr53zpd5uozM61XqcM8Oq35FHZhRQ +2b9riDax3zXtYu3pplGLMZmrouQhTKNU5tI/0gsQXUCqMrR9gyQkhkQHAN5CZYU7 +LFEcG5OAvsx/i7XSs5gLg3kaERCdEOUxQ/AW+/BTE7iGN0D6KPH6VUSu6VoNCrTK +PmYvgta7hwebnvo65/OAc4inp9C19FUkhcNbaCKduWBgUt348+IzVEw9H8+PrdVZ +dYGfVXAsDFY3zz0ThUbaZ52XS1pCCQ1Df9bQnTgqJNc+u1xQHLYAageKS83uAbtS +nYjBFFuxeRR2FA1n8echCWQV+16Kqq31U1E2yLfWcQKBgQDSoT73pO9h/yN5myqu +XxhAC+Ndas0DTl4pmVPpybpenJerba/0KCfYpcSFHAdkXZ1DYL7U+9uh5NRul09f +WdjayFjn0vv63rwX+PGi1yPHTIv5kLvjYXJtaxzxSzQivYMPmD/7QX4tEsUkpJ8k +90vMSS/C5ieWbpFwWVvEjFbqHQKBgQDNOsTq6USE3R8p4lj9Ror/waUuWVdzZnm3 +uZGJw3WzvzaXmqLSfVHntUnD8TPHgk3WZxDhrF73POMADkl9IN/JPI150/Uo6YJo +qYGoZr0mmnEZxVCkwODz5C9icnyjklcRdIRM6eljhFMQDVEacDkptsntHUyIdQZc +L2eLNUfEgwKBgHxy7UNg3lemag110rgIU8mzvHj7m3oymYw2nc/qcwVnvG17d5Tp +DPICr6R+NRfl//9JcDdjQBfdnm5hVHJgIbLS4UTH8j390GDRo+O0/dzJq4KfM4Rb +lUJ1ITqoVnuYQZG7QUJxJd330yedZLJwswZWz7N2TTmixqf9BC2TRd85AoGAN+Qh +bLhKaMSvkACMq61ifXSHP7AlGNB3pYlsEVCh5WnVvEPow9pNTAUbKbmumE7sU8+N +0WfYFQ0H5SP+74zcZTmQbfVDdvjhAw/mt64DJVg6JQKPi87bdJBYNz9mokVgYOiS +fz/Ux71pwZ1e0QxvBOU66NBp31+/c6uVT1wbR3ECgYAdye1+UPpS9Dn89g6Ks0kv +UaFKykXu7vY2uxiNqhmWzze4iq5wmIHmEwc6+rVMluXQPAME7Iya3mBmto9AHQ/n +/ka+fGoaUgAojCLZW5DZcelIETw+Dk+95vyyAUsWfAvn4nKo4/rkBXcSHlvgElzq +SorPiBWYosFB6jqUTXew2w== +-----END PRIVATE KEY----- +`, role)) + } else { + testPEM = []byte(fmt.Sprintf(`-----BEGIN PRIVATE KEY----- +gun: %s +role: %s +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCo23n5TrVazr9C +DniRin4uSxx9w4tDtp5WbvACE0iWw2T0l6GHPnKa6aBFmJ3GxRQtwveM+cQVub3A +KjIS7OdJpeQA5fOrpnC5dgv5l/DbmZ5SHrzAm9JgYmrw7Uj1dyeQ6jpPra4ChF7P +YOIcoeTp4eWOSJzztZQyftnBLVRgTIYXXX1kVZVfWVFsT2FQk7ei9Gw/UnslyVQd +HZxYa98SAmsoQ5YZb3I11Tk1LHsCS6Py9p6tL3vdyW9rJioqNu2RhO/WwhqiSttd +/xTanwJRlD5IWE32CU3II4UtaZZrYDeimRekVV7zqwgTBAzNsmatZhD1o6E9LZ9D +JFhKwwDXAgMBAAECggEAbqa0PV0IlqMYze6xr53zpd5uozM61XqcM8Oq35FHZhRQ +2b9riDax3zXtYu3pplGLMZmrouQhTKNU5tI/0gsQXUCqMrR9gyQkhkQHAN5CZYU7 +LFEcG5OAvsx/i7XSs5gLg3kaERCdEOUxQ/AW+/BTE7iGN0D6KPH6VUSu6VoNCrTK +PmYvgta7hwebnvo65/OAc4inp9C19FUkhcNbaCKduWBgUt348+IzVEw9H8+PrdVZ +dYGfVXAsDFY3zz0ThUbaZ52XS1pCCQ1Df9bQnTgqJNc+u1xQHLYAageKS83uAbtS +nYjBFFuxeRR2FA1n8echCWQV+16Kqq31U1E2yLfWcQKBgQDSoT73pO9h/yN5myqu +XxhAC+Ndas0DTl4pmVPpybpenJerba/0KCfYpcSFHAdkXZ1DYL7U+9uh5NRul09f +WdjayFjn0vv63rwX+PGi1yPHTIv5kLvjYXJtaxzxSzQivYMPmD/7QX4tEsUkpJ8k +90vMSS/C5ieWbpFwWVvEjFbqHQKBgQDNOsTq6USE3R8p4lj9Ror/waUuWVdzZnm3 +uZGJw3WzvzaXmqLSfVHntUnD8TPHgk3WZxDhrF73POMADkl9IN/JPI150/Uo6YJo +qYGoZr0mmnEZxVCkwODz5C9icnyjklcRdIRM6eljhFMQDVEacDkptsntHUyIdQZc +L2eLNUfEgwKBgHxy7UNg3lemag110rgIU8mzvHj7m3oymYw2nc/qcwVnvG17d5Tp +DPICr6R+NRfl//9JcDdjQBfdnm5hVHJgIbLS4UTH8j390GDRo+O0/dzJq4KfM4Rb +lUJ1ITqoVnuYQZG7QUJxJd330yedZLJwswZWz7N2TTmixqf9BC2TRd85AoGAN+Qh +bLhKaMSvkACMq61ifXSHP7AlGNB3pYlsEVCh5WnVvEPow9pNTAUbKbmumE7sU8+N +0WfYFQ0H5SP+74zcZTmQbfVDdvjhAw/mt64DJVg6JQKPi87bdJBYNz9mokVgYOiS +fz/Ux71pwZ1e0QxvBOU66NBp31+/c6uVT1wbR3ECgYAdye1+UPpS9Dn89g6Ks0kv +UaFKykXu7vY2uxiNqhmWzze4iq5wmIHmEwc6+rVMluXQPAME7Iya3mBmto9AHQ/n +/ka+fGoaUgAojCLZW5DZcelIETw+Dk+95vyyAUsWfAvn4nKo4/rkBXcSHlvgElzq +SorPiBWYosFB6jqUTXew2w== +-----END PRIVATE KEY----- +`, gun, role)) + } + + return testPEM +} + func TestValidateCertificateWithSHA1(t *testing.T) { // Test against SHA1 signature algorithm cert first startTime := time.Now() diff --git a/utils/keys.go b/utils/keys.go index 581f11f34..b2cacb6aa 100644 --- a/utils/keys.go +++ b/utils/keys.go @@ -132,7 +132,7 @@ func ImportKeys(from io.Reader, to []Importer, fallbackRole string, fallbackGUN return errors.New("maximum number of passphrase attempts exceeded") } } - blockBytes, err = utils.EncryptPrivateKey(privKey, tufdata.RoleName(block.Headers["role"]), tufdata.GUN(block.Headers["gun"]), chosenPassphrase) + blockBytes, err = utils.ConvertPrivateKeyToPKCS8(privKey, tufdata.RoleName(block.Headers["role"]), tufdata.GUN(block.Headers["gun"]), chosenPassphrase) if err != nil { return errors.New("failed to encrypt key with given passphrase") } diff --git a/utils/keys_test.go b/utils/keys_test.go index 90bd4cd5c..ee9579e2a 100644 --- a/utils/keys_test.go +++ b/utils/keys_test.go @@ -494,7 +494,7 @@ func TestEncryptedKeyImportFail(t *testing.T) { privKey, err := utils.GenerateECDSAKey(rand.Reader) require.NoError(t, err) - pemBytes, err := utils.EncryptPrivateKey(privKey, data.CanonicalRootRole, "", cannedPassphrase) + pemBytes, err := utils.ConvertPrivateKeyToPKCS8(privKey, data.CanonicalRootRole, "", cannedPassphrase) require.NoError(t, err) in := bytes.NewBuffer(pemBytes) @@ -511,7 +511,7 @@ func TestEncryptedKeyImportSuccess(t *testing.T) { originalKey := privKey.Private() require.NoError(t, err) - pemBytes, err := utils.EncryptPrivateKey(privKey, data.CanonicalSnapshotRole, "somegun", cannedPassphrase) + pemBytes, err := utils.ConvertPrivateKeyToPKCS8(privKey, data.CanonicalSnapshotRole, "somegun", cannedPassphrase) require.NoError(t, err) b, _ := pem.Decode(pemBytes) @@ -547,7 +547,7 @@ func TestEncryption(t *testing.T) { originalKey := privKey.Private() require.NoError(t, err) - pemBytes, err := utils.EncryptPrivateKey(privKey, "", "", "") + pemBytes, err := utils.ConvertPrivateKeyToPKCS8(privKey, "", "", "") require.NoError(t, err) in := bytes.NewBuffer(pemBytes)