Merge pull request #83 from docker/cooking-the-tofu-to-support-rotations

Cooking the tofu to support rotations
This commit is contained in:
David Lawrence 2015-07-19 14:50:39 -07:00
commit 9d31d343f3
7 changed files with 414 additions and 146 deletions

2
Godeps/Godeps.json generated
View File

@ -63,7 +63,7 @@
},
{
"ImportPath": "github.com/endophage/gotuf",
"Rev": "a8a23ab6e67bd0e9fbaf563aabd9e6ee7ea344d2"
"Rev": "9640c9b3f2ff0ba75baf7d1a57632e16cb78d5e6"
},
{
"ImportPath": "github.com/go-sql-driver/mysql",

View File

@ -52,7 +52,13 @@ func VerifyRoot(s *data.Signed, minVersion int, keys map[string]data.PublicKey)
continue
}
if err := verifier.Verify(keys[sig.KeyID], sig.Signature, msg); err != nil {
key, ok := keys[sig.KeyID]
if !ok {
logrus.Debugf("continuing b/c signing key isn't present in keys: %s\n", sig.KeyID)
continue
}
if err := verifier.Verify(key, sig.Signature, msg); err != nil {
logrus.Debugf("continuing b/c signature was invalid\n")
continue
}

View File

@ -3,7 +3,6 @@ package client
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"html/template"
"io/ioutil"
@ -125,7 +124,7 @@ func validateRootSuccessfully(t *testing.T, rootType data.KeyAlgorithm) {
assert.Len(t, repo.KeyStoreManager.TrustedCertificateStore().GetCertificates(), 1)
//
// Test certificate mismatch logic. We remove all certs, and a different cert to the
// Test certificate mismatch logic. We remove all certs, add a different cert to the
// same CN, and expect ValidateRoot to fail
//
@ -142,7 +141,7 @@ func validateRootSuccessfully(t *testing.T, rootType data.KeyAlgorithm) {
// in the store for the dnsName docker.com/notary, so TOFUS doesn't apply
_, err = repo.ListTargets()
if assert.Error(t, err, "An error was expected") {
assert.Equal(t, err, keystoremanager.ErrValidationFail)
assert.Equal(t, err, &keystoremanager.ErrValidationFail{Reason: "failed to validate integrity of roots"})
}
//
@ -187,7 +186,7 @@ func validateRootSuccessfully(t *testing.T, rootType data.KeyAlgorithm) {
//
err = repo.KeyStoreManager.ValidateRoot(&testSignedRoot, "secure.example.com")
if assert.Error(t, err, "An error was expected") {
assert.Equal(t, err, errors.New("tuf: valid signatures did not meet threshold"))
assert.Equal(t, err, &keystoremanager.ErrValidationFail{Reason: "failed to validate integrity of roots"})
}
}
@ -224,7 +223,7 @@ func TestValidateRootWithInvalidData(t *testing.T) {
//
err = keyStoreManager.ValidateRoot(&testSignedRoot, "diogomonica.com/notary")
if assert.Error(t, err, "An error was expected") {
assert.Equal(t, err, keystoremanager.ErrValidationFail)
assert.Equal(t, err, &keystoremanager.ErrValidationFail{Reason: "unable to retrieve valid leaf certificates"})
}
//
@ -252,7 +251,7 @@ func TestValidateRootWithInvalidData(t *testing.T) {
err = keyStoreManager.ValidateRoot(&testSignedRoot, "docker.com/notary")
if assert.Error(t, err, "An error was expected") {
assert.Equal(t, err, keystoremanager.ErrValidationFail)
assert.Equal(t, err, &keystoremanager.ErrValidationFail{Reason: "unable to retrieve valid leaf certificates"})
}
//
@ -268,6 +267,6 @@ func TestValidateRootWithInvalidData(t *testing.T) {
err = keyStoreManager.ValidateRoot(&testSignedRoot, "docker.com/notary")
if assert.Error(t, err, "An error was expected") {
assert.Equal(t, err, keystoremanager.ErrValidationFail)
assert.Equal(t, err, &keystoremanager.ErrValidationFail{Reason: "unable to retrieve valid leaf certificates"})
}
}

View File

@ -34,14 +34,29 @@ const (
rsaRootKeySize = 4096 // Used for new root keys
)
var (
// ErrValidationFail is returned when there is no trusted certificate in any of the
// root keys available in the roots.json
ErrValidationFail = errors.New("could not validate the path to a trusted root")
// ErrRootRotationFail is returned when we fail to do a full root key rotation
// by either failing to add the new root certificate, or delete the old ones
ErrRootRotationFail = errors.New("could not rotate trust to a new trusted root")
)
// ErrValidationFail is returned when there is no valid trusted certificates
// being served inside of the roots.json
type ErrValidationFail struct {
Reason string
}
// ErrValidationFail is returned when there is no valid trusted certificates
// being served inside of the roots.json
func (err ErrValidationFail) Error() string {
return fmt.Sprintf("could not validate the path to a trusted root: %s", err.Reason)
}
// ErrRootRotationFail is returned when we fail to do a full root key rotation
// by either failing to add the new root certificate, or delete the old ones
type ErrRootRotationFail struct {
Reason string
}
// ErrRootRotationFail is returned when we fail to do a full root key rotation
// by either failing to add the new root certificate, or delete the old ones
func (err ErrRootRotationFail) Error() string {
return fmt.Sprintf("could not rotate trust to a new trusted root: %s", err.Reason)
}
// NewKeyStoreManager returns an initialized KeyStoreManager, or an error
// if it fails to create the KeyFileStores or load certificates
@ -168,172 +183,241 @@ func (km *KeyStoreManager) GetRootCryptoService(rootKeyID, passphrase string) (*
}
/*
ValidateRoot iterates over every root key included in the TUF data and
attempts to validate the certificate by first checking for an exact match on
the certificate store, and subsequently trying to find a valid chain on the
trustedCAStore.
ValidateRoot receives a new root, validates its correctness and attempts to
do root key rotation if needed.
Currently this method operates on a Trust On First Use (TOFU) model: if we
First we list the current trusted certificates we have for a particular GUN. If
that list is non-empty means that we've already seen this repository before, and
have a list of trusted certificates for it. In this case, we use this list of
certificates to attempt to validate this root file.
If the previous validation suceeds, or in the case where we found no trusted
certificates for this particular GUN, we check the integrity of the root by
making sure that it is validated by itself. This means that we will attempt to
validate the root data with the certificates that are included in the root keys
themselves.
If this last steps succeeds, we attempt to do root rotation, by ensuring that
we only trust the certificates that are present in the new root.
This mechanism of operation is essentially Trust On First Use (TOFU): if we
have never seen a certificate for a particular CN, we trust it. If later we see
a different certificate for that certificate, we return an ErrValidationFailed error.
Note that since we only allow trust data to be downloaded over an HTTPS channel
we are using the current web-of-trust to validate the first download of the certificate
we are using the current public PKI to validate the first download of the certificate
adding an extra layer of security over the normal (SSH style) trust model.
We shall call this: TOFUS.
ValidateRoot also supports root key rotation, trusting a new certificate that has
been included in the roots.json, and removing trust in the old one.
*/
func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error {
logrus.Debugf("entered ValidateRoot with dns: %s", dnsName)
rootSigned, err := data.RootFromSigned(root)
func (km *KeyStoreManager) ValidateRoot(root *data.Signed, gun string) error {
logrus.Debugf("entered ValidateRoot with dns: %s", gun)
signedRoot, err := data.RootFromSigned(root)
if err != nil {
return err
}
// validKeys will store all the keys that were considered valid either by
// direct certificate match, or CA chain path
validKeys := make(map[string]data.PublicKey)
// allCerts will keep a list of all leafCerts that were found, and is used
// to aid on root certificate rotation
allCerts := make(map[string]*x509.Certificate)
// Before we loop through all root keys available, make sure any exist
rootRoles, ok := rootSigned.Signed.Roles["root"]
if !ok {
return errors.New("no root roles found in tuf metadata")
// Retrieve all the leaf certificates in root for which the CN matches the GUN
allValidCerts, err := validRootLeafCerts(signedRoot, gun)
if err != nil {
logrus.Debugf("error retrieving valid leaf certificates for: %s, %v", gun, err)
return &ErrValidationFail{Reason: "unable to retrieve valid leaf certificates"}
}
logrus.Debugf("found the following root keys in roots.json: %v", rootRoles.KeyIDs)
// Retrieve all the trusted certificates that match this gun
certsForCN, err := km.trustedCertificateStore.GetCertificatesByCN(gun)
if err != nil {
// If the error that we get back is different than ErrNoCertificatesFound
// we couldn't check if there are any certificates with this CN already
// trusted. Let's take the conservative approach and return a failed validation
if _, ok := err.(*trustmanager.ErrNoCertificatesFound); !ok {
logrus.Debugf("error retrieving trusted certificates for: %s, %v", gun, err)
return &ErrValidationFail{Reason: "unable to retrieve trusted certificates"}
}
}
// If we have certificates that match this specific GUN, let's make sure to
// use them first to validate that this new root is valid.
if len(certsForCN) != 0 {
logrus.Debugf("found %d valid certificates for %s", len(certsForCN), gun)
err = signed.VerifyRoot(root, 0, trustmanager.CertsToKeys(certsForCN))
if err != nil {
logrus.Debugf("failed to verify TUF data for: %s, %v", gun, err)
return &ErrValidationFail{Reason: "failed to validate integrity of roots"}
}
}
// Validate the integrity of the new root (does it have valid signatures)
err = signed.VerifyRoot(root, 0, trustmanager.CertsToKeys(allValidCerts))
if err != nil {
logrus.Debugf("failed to verify TUF data for: %s, %v", gun, err)
return &ErrValidationFail{Reason: "failed to validate integrity of roots"}
}
// Getting here means A) we had trusted certificates and both the
// old and new validated this root; or B) we had no trusted certificates but
// the new set of certificates has integrity (self-signed)
logrus.Debugf("entering root certificate rotation for: %s", gun)
// Do root certificate rotation: we trust only the certs present in the new root
// First we add all the new certificates (even if they already exist)
for _, cert := range allValidCerts {
err := km.trustedCertificateStore.AddCert(cert)
if err != nil {
// If the error is already exists we don't fail the rotation
if _, ok := err.(*trustmanager.ErrCertExists); ok {
continue
}
logrus.Debugf("error adding new trusted certificate for: %s, %v", gun, err)
}
// Getting the CertID for debug only, don't care about the error
certID, _ := trustmanager.FingerprintCert(cert)
logrus.Debugf("adding trust certificate with certID %s for %s", certID, gun)
}
// Now we delete old certificates that aren't present in the new root
for certID, cert := range certsToRemove(certsForCN, allValidCerts) {
logrus.Debugf("removing certificate with certID: %s", certID)
err = km.trustedCertificateStore.RemoveCert(cert)
if err != nil {
logrus.Debugf("failed to remove trusted certificate with keyID: %s, %v", certID, err)
return &ErrRootRotationFail{Reason: "failed to rotate root keys"}
}
}
logrus.Debugf("Root validation succeeded for %s", gun)
return nil
}
// validRootLeafCerts returns a list of non-exipired, non-sha1 certificates whoose
// Common-Names match the provided GUN
func validRootLeafCerts(root *data.SignedRoot, gun string) ([]*x509.Certificate, error) {
// Get a list of all of the leaf certificates present in root
allLeafCerts, _ := parseAllCerts(root)
var validLeafCerts []*x509.Certificate
// Go through every leaf certificate and check that the CN matches the gun
for _, cert := range allLeafCerts {
// Validate that this leaf certificate has a CN that matches the exact gun
if cert.Subject.CommonName != gun {
logrus.Debugf("error leaf certificate CN: %s doesn't match the given GUN: %s", cert.Subject.CommonName)
continue
}
// Make sure the certificate is not expired
if time.Now().After(cert.NotAfter) {
logrus.Debugf("error leaf certificate is expired")
continue
}
// We don't allow root certificates that use SHA1
if cert.SignatureAlgorithm == x509.SHA1WithRSA ||
cert.SignatureAlgorithm == x509.DSAWithSHA1 ||
cert.SignatureAlgorithm == x509.ECDSAWithSHA1 {
logrus.Debugf("error certificate uses deprecated hashing algorithm (SHA1)")
continue
}
validLeafCerts = append(validLeafCerts, cert)
}
if len(validLeafCerts) < 1 {
return nil, errors.New("no valid leaf certificates found in any of the root keys")
}
return validLeafCerts, nil
}
// parseAllCerts returns two maps, one with all of the leafCertificates and one
// with all the intermediate certificates found in signedRoot
func parseAllCerts(signedRoot *data.SignedRoot) (map[string]*x509.Certificate, map[string][]*x509.Certificate) {
leafCerts := make(map[string]*x509.Certificate)
intCerts := make(map[string][]*x509.Certificate)
// Before we loop through all root keys available, make sure any exist
rootRoles, ok := signedRoot.Signed.Roles["root"]
if !ok {
logrus.Debugf("tried to parse certificates from invalid root signed data")
return nil, nil
}
logrus.Debugf("found the following root keys: %v", rootRoles.KeyIDs)
// Iterate over every keyID for the root role inside of roots.json
for _, keyID := range rootRoles.KeyIDs {
// check that the key exists in the signed root keys map
key, ok := signedRoot.Signed.Keys[keyID]
if !ok {
logrus.Debugf("error while getting data for keyID: %s", keyID)
continue
}
// Decode all the x509 certificates that were bundled with this
// Specific root key
decodedCerts, err := trustmanager.LoadCertBundleFromPEM([]byte(rootSigned.Signed.Keys[keyID].Public()))
decodedCerts, err := trustmanager.LoadCertBundleFromPEM(key.Public())
if err != nil {
logrus.Debugf("error while parsing root certificate with keyID: %s, %v", keyID, err)
continue
}
// Get all non-CA certificates in the decoded certificates
leafCerts := trustmanager.GetLeafCerts(decodedCerts)
leafCertList := trustmanager.GetLeafCerts(decodedCerts)
// If we got no leaf certificates or we got more than one, fail
if len(leafCerts) != 1 {
logrus.Debugf("wasn't able to find a leaf certificate in the chain of keyID: %s", keyID)
if len(leafCertList) != 1 {
logrus.Debugf("invalid chain due to leaf certificate missing or too many leaf certificates for keyID: %s", keyID)
continue
}
// Get the ID of the leaf certificate
leafCert := leafCerts[0]
leafCert := leafCertList[0]
leafID, err := trustmanager.FingerprintCert(leafCert)
if err != nil {
logrus.Debugf("error while fingerprinting root certificate with keyID: %s, %v", keyID, err)
continue
}
// Validate that this leaf certificate has a CN that matches the exact gun
if leafCert.Subject.CommonName != dnsName {
logrus.Debugf("error leaf certificate CN: %s doesn't match the given dns name: %s", leafCert.Subject.CommonName, dnsName)
// Store the leaf cert in the map
leafCerts[leafID] = leafCert
// Get all the remainder certificates marked as a CA to be used as intermediates
intermediateCerts := trustmanager.GetIntermediateCerts(decodedCerts)
intCerts[leafID] = intermediateCerts
}
return leafCerts, intCerts
}
// certsToRemove returns all the certifificates from oldCerts that aren't present
// in newCerts
func certsToRemove(oldCerts, newCerts []*x509.Certificate) map[string]*x509.Certificate {
certsToRemove := make(map[string]*x509.Certificate)
// If no newCerts were provided
if len(newCerts) == 0 {
return certsToRemove
}
// Populate a map with all the IDs from newCert
var newCertMap = make(map[string]struct{})
for _, cert := range newCerts {
certID, err := trustmanager.FingerprintCert(cert)
if err != nil {
logrus.Debugf("error while fingerprinting root certificate with keyID: %s, %v", certID, err)
continue
}
newCertMap[certID] = struct{}{}
}
// Add all the valid leafs to the certificates map so we can refer to them later
allCerts[leafID] = leafCert
// Retrieve all the trusted certificates that match this dns Name
certsForCN, err := km.trustedCertificateStore.GetCertificatesByCN(dnsName)
// Iterate over all the old certificates and check to see if we should remove them
for _, cert := range oldCerts {
certID, err := trustmanager.FingerprintCert(cert)
if err != nil {
// If the error that we get back is different than ErrNoCertificatesFound
// we couldn't check if there are any certificates with this CN already
// trusted. Let's take the conservative approach and not trust this key
if _, ok := err.(*trustmanager.ErrNoCertificatesFound); !ok {
logrus.Debugf("error retrieving certificates for: %s, %v", dnsName, err)
continue
}
logrus.Debugf("error while fingerprinting root certificate with certID: %s, %v", certID, err)
continue
}
// If there are no certificates with this CN, lets TOFUS!
// Note that this logic should only exist in docker 1.8
if len(certsForCN) == 0 {
km.trustedCertificateStore.AddCert(leafCert)
certsForCN = append(certsForCN, leafCert)
logrus.Debugf("using TOFUS on %s with keyID: %s", dnsName, leafID)
}
// Iterate over all known certificates for this CN and see if any are trusted
for _, cert := range certsForCN {
// Check to see if there is an exact match of this certificate.
certID, err := trustmanager.FingerprintCert(cert)
if err == nil && certID == leafID {
validKeys[keyID] = rootSigned.Signed.Keys[keyID]
logrus.Debugf("found an exact match for %s with keyID: %s", dnsName, keyID)
}
}
// Check to see if this leafCertificate has a chain to one of the Root
// CAs of our CA Store.
err = trustmanager.Verify(km.trustedCAStore, dnsName, decodedCerts)
if err == nil {
validKeys[keyID] = rootSigned.Signed.Keys[keyID]
logrus.Debugf("found a CA path for %s with keyID: %s", dnsName, keyID)
if _, ok := newCertMap[certID]; !ok {
certsToRemove[certID] = cert
}
}
if len(validKeys) < 1 {
logrus.Debugf("wasn't able to trust any of the root keys")
return ErrValidationFail
}
// TODO(david): change hardcoded minversion on TUF.
err = signed.VerifyRoot(root, 0, validKeys)
if err != nil {
return err
}
var newRootKey data.PublicKey
// VerifyRoot returns a non-nil value if there is a root key rotation happening.
// If this happens, we should replace the old root of trust with the new one
if newRootKey != nil {
logrus.Debugf("got a new root key to rotate to: %s", newRootKey.ID())
// Retrieve the certificate associated with the new root key and trust it
newRootKeyCert, ok := allCerts[newRootKey.ID()]
// Paranoid check for the certificate still being in the map
if !ok {
logrus.Debugf("error while retrieving new root certificate with keyID: %s, %v", newRootKey.ID(), err)
return ErrRootRotationFail
}
// Add the new root certificate to our certificate store
err := km.trustedCertificateStore.AddCert(newRootKeyCert)
if err != nil {
// Ignore the error if the certificate already exists
if _, ok := err.(*trustmanager.ErrCertExists); !ok {
logrus.Debugf("error while adding new root certificate with keyID: %s, %v", newRootKey.ID(), err)
return ErrRootRotationFail
}
logrus.Debugf("root certificate already exists in keystore: %s", newRootKey.ID())
}
// Remove the new root certificate from the certificate mapping so we
// can remove trust from all of the remaining ones
delete(allCerts, newRootKey.ID())
// Iterate over all old valid certificates and remove them, essentially
// finishing the rotation of the currently trusted root certificate
for _, cert := range allCerts {
err := km.trustedCertificateStore.RemoveCert(cert)
if err != nil {
logrus.Debugf("error while removing old root certificate: %v", err)
return ErrRootRotationFail
}
logrus.Debugf("removed trust from old root certificate")
}
}
logrus.Debugf("Root validation succeeded")
return nil
return certsToRemove
}

View File

@ -121,7 +121,7 @@ func (s *X509FileStore) RemoveCert(cert *x509.Certificate) error {
filename := s.fileMap[certID]
delete(s.fileMap, certID)
name := string(cert.RawSubject)
name := string(cert.Subject.CommonName)
// Filter the fingerprint out of this name entry
fpList := s.nameMap[name]

View File

@ -114,8 +114,6 @@ func fingerprintCert(cert *x509.Certificate) (CertID, error) {
// Create new TUF Key so we can compute the TUF-compliant CertID
tufKey := data.NewPublicKey(keyType, pemdata)
logrus.Debugf("certificate fingerprint generated for key type %s: %s", keyType, tufKey.ID())
return CertID(tufKey.ID()), nil
}
@ -189,6 +187,17 @@ func GetLeafCerts(certs []*x509.Certificate) []*x509.Certificate {
return leafCerts
}
// GetIntermediateCerts parses a list of x509 Certificates and returns all of the
// ones marked as a CA, to be used as intermediates
func GetIntermediateCerts(certs []*x509.Certificate) (intCerts []*x509.Certificate) {
for _, cert := range certs {
if cert.IsCA {
intCerts = append(intCerts, cert)
}
}
return intCerts
}
// 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) {
@ -420,6 +429,32 @@ func EncryptPrivateKey(key data.PrivateKey, passphrase string) ([]byte, error) {
return pem.EncodeToMemory(encryptedPEMBlock), nil
}
// CertsToKeys transforms each of the input certificates into it's corresponding
// PublicKey
func CertsToKeys(certs []*x509.Certificate) map[string]data.PublicKey {
keys := make(map[string]data.PublicKey)
for _, cert := range certs {
block := pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}
pemdata := pem.EncodeToMemory(&block)
var keyType data.KeyAlgorithm
switch cert.PublicKeyAlgorithm {
case x509.RSA:
keyType = data.RSAx509Key
case x509.ECDSA:
keyType = data.ECDSAx509Key
default:
logrus.Debugf("unknown certificate type found, ignoring")
}
// Create new the appropriate PublicKey
newKey := data.NewPublicKey(keyType, pemdata)
keys[newKey.ID()] = newKey
}
return keys
}
// NewCertificate returns an X509 Certificate following a template, given a GUN.
func NewCertificate(gun string) (*x509.Certificate, error) {
notBefore := time.Now()
@ -436,8 +471,7 @@ func NewCertificate(gun string) (*x509.Certificate, error) {
return &x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
Organization: []string{gun},
CommonName: gun,
CommonName: gun,
},
NotBefore: notBefore,
NotAfter: notAfter,

View File

@ -0,0 +1,145 @@
package trustmanager
import (
"crypto/rand"
"crypto/x509"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestCertsToKeys(t *testing.T) {
// Get root certificate
rootCA, err := LoadCertFromFile("../fixtures/root-ca.crt")
assert.NoError(t, err)
// Get intermediate certificate
intermediateCA, err := LoadCertFromFile("../fixtures/intermediate-ca.crt")
assert.NoError(t, err)
// Get leaf certificate
leafCert, err := LoadCertFromFile("../fixtures/secure.example.com.crt")
assert.NoError(t, err)
// Get our certList with Leaf Cert and Intermediate
certList := []*x509.Certificate{leafCert, intermediateCA, rootCA}
// Call CertsToKEys
keys := CertsToKeys(certList)
assert.NotNil(t, keys)
assert.Len(t, keys, 3)
// Call GetLeafCerts
newKeys := GetLeafCerts(certList)
assert.NotNil(t, newKeys)
assert.Len(t, newKeys, 1)
// Call GetIntermediateCerts (checks for certs with IsCA true)
newKeys = GetIntermediateCerts(certList)
assert.NotNil(t, newKeys)
assert.Len(t, newKeys, 2)
}
func TestNewCertificate(t *testing.T) {
cert, err := NewCertificate("docker.com/alpine")
assert.NoError(t, err)
assert.Equal(t, cert.Subject.CommonName, "docker.com/alpine")
assert.True(t, time.Now().Before(cert.NotAfter))
}
func TestKeyOperations(t *testing.T) {
// Generate our ED25519 private key
edKey, err := GenerateED25519Key(rand.Reader)
assert.NoError(t, err)
// Generate our EC private key
ecKey, err := GenerateECDSAKey(rand.Reader)
assert.NoError(t, err)
// Generate our RSA private key
rsaKey, err := GenerateRSAKey(rand.Reader, 512)
// Encode our ED private key
edPEM, err := KeyToPEM(edKey)
assert.NoError(t, err)
// Encode our EC private key
ecPEM, err := KeyToPEM(ecKey)
assert.NoError(t, err)
// Encode our RSA private key
rsaPEM, err := KeyToPEM(rsaKey)
assert.NoError(t, err)
// Check to see if ED key it is encoded
stringEncodedEDKey := string(edPEM)
assert.True(t, strings.Contains(stringEncodedEDKey, "-----BEGIN ED25519 PRIVATE KEY-----"))
// Check to see if EC key it is encoded
stringEncodedECKey := string(ecPEM)
assert.True(t, strings.Contains(stringEncodedECKey, "-----BEGIN EC PRIVATE KEY-----"))
// Check to see if RSA key it is encoded
stringEncodedRSAKey := string(rsaPEM)
assert.True(t, strings.Contains(stringEncodedRSAKey, "-----BEGIN RSA PRIVATE KEY-----"))
// Decode our ED Key
decodedEDKey, err := ParsePEMPrivateKey(edPEM, "")
assert.NoError(t, err)
assert.Equal(t, edKey.Private(), decodedEDKey.Private())
// Decode our EC Key
decodedECKey, err := ParsePEMPrivateKey(ecPEM, "")
assert.NoError(t, err)
assert.Equal(t, ecKey.Private(), decodedECKey.Private())
// Decode our RSA Key
decodedRSAKey, err := ParsePEMPrivateKey(rsaPEM, "")
assert.NoError(t, err)
assert.Equal(t, rsaKey.Private(), decodedRSAKey.Private())
// Encrypt our ED Key
encryptedEDKey, err := EncryptPrivateKey(edKey, "ponies")
assert.NoError(t, err)
// Encrypt our EC Key
encryptedECKey, err := EncryptPrivateKey(ecKey, "ponies")
assert.NoError(t, err)
// Encrypt our RSA Key
encryptedRSAKey, err := EncryptPrivateKey(rsaKey, "ponies")
assert.NoError(t, err)
// Check to see if ED key it is encrypted
stringEncryptedEDKey := string(encryptedEDKey)
assert.True(t, strings.Contains(stringEncryptedEDKey, "-----BEGIN ED25519 PRIVATE KEY-----"))
assert.True(t, strings.Contains(stringEncryptedEDKey, "Proc-Type: 4,ENCRYPTED"))
// Check to see if EC key it is encrypted
stringEncryptedECKey := string(encryptedECKey)
assert.True(t, strings.Contains(stringEncryptedECKey, "-----BEGIN EC PRIVATE KEY-----"))
assert.True(t, strings.Contains(stringEncryptedECKey, "Proc-Type: 4,ENCRYPTED"))
// Check to see if RSA key it is encrypted
stringEncryptedRSAKey := string(encryptedRSAKey)
assert.True(t, strings.Contains(stringEncryptedRSAKey, "-----BEGIN RSA PRIVATE KEY-----"))
assert.True(t, strings.Contains(stringEncryptedRSAKey, "Proc-Type: 4,ENCRYPTED"))
// Decrypt our ED Key
decryptedEDKey, err := ParsePEMPrivateKey(encryptedEDKey, "ponies")
assert.NoError(t, err)
assert.Equal(t, edKey.Private(), decryptedEDKey.Private())
// Decrypt our EC Key
decryptedECKey, err := ParsePEMPrivateKey(encryptedECKey, "ponies")
assert.NoError(t, err)
assert.Equal(t, ecKey.Private(), decryptedECKey.Private())
// Decrypt our RSA Key
decryptedRSAKey, err := ParsePEMPrivateKey(encryptedRSAKey, "ponies")
assert.NoError(t, err)
assert.Equal(t, rsaKey.Private(), decryptedRSAKey.Private())
}