1
0
mirror of https://github.com/docker/docs.git synced 2026-10-31 10:34:26 +03:00

Working ECDSA implementation

Signed-off-by: Diogo Monica <diogo@docker.com>
This commit is contained in:
Diogo Monica
2015-07-11 19:44:14 -07:00
parent 43d0ec8a75
commit 39482c2397
5 changed files with 212 additions and 78 deletions

View File

@@ -11,6 +11,7 @@ import (
"path/filepath"
"strings"
"github.com/Sirupsen/logrus"
"github.com/docker/notary/trustmanager"
"github.com/endophage/gotuf/data"
)
@@ -46,32 +47,41 @@ func (ccs *RSACryptoService) Create(role string) (*data.PublicKey, error) {
}
// Store the private key into our keystore with the name being: /GUN/ID.key
ccs.keyStore.AddKey(filepath.Join(ccs.gun, privKey.ID()), privKey)
err = ccs.keyStore.AddKey(filepath.Join(ccs.gun, privKey.ID()), privKey)
if err != nil {
return nil, fmt.Errorf("failed to add key to filestore: %v", err)
}
logrus.Debugf("generated new RSA key for role: %s and keyID: %s", role, privKey.ID())
return data.PublicKeyFromPrivate(*privKey), nil
}
// Sign returns the signatures for data with the given root Key ID, falling back
// if not rootKeyID is found
// Sign returns the signatures for the payload with a set of keyIDs. It ignores
// errors to sign and expects the called to validate if the number of returned
// signatures is adequate.
func (ccs *RSACryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signature, error) {
// Create hasher and hash data
hash := crypto.SHA256
hashed := sha256.Sum256(payload)
signatures := make([]data.Signature, 0, len(keyIDs))
for _, fingerprint := range keyIDs {
for _, keyid := range keyIDs {
// ccs.gun will be empty if this is the root key
keyName := filepath.Join(ccs.gun, fingerprint)
keyName := filepath.Join(ccs.gun, keyid)
var privKey *data.PrivateKey
var err error
var method string
// Read PrivateKey from file.
// This assumes root keys always have to have a non-empty passphrase.
// TODO(diogo): This assumes both that only root keys are encrypted and
// that encrypted keys are always X509 encoded
if ccs.passphrase != "" {
// This is a root key
privKey, err = ccs.keyStore.GetDecryptedKey(keyName, ccs.passphrase)
// This method is added to the signature so verifiers can decode
// the X509 certificate before trying to parse the public materials
method = "RSASSA-PSS-X509"
} else {
privKey, err = ccs.keyStore.GetKey(keyName)
@@ -82,17 +92,26 @@ func (ccs *RSACryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signa
// InitRepo gets a signer that doesn't have access to
// the root keys. Continuing here is safe because we
// end up not returning any signatures.
logrus.Debugf("ignoring error attempting to retrieve RSA key ID: %s, %v", privKey.ID(), err)
continue
}
sig, err := rsaSign(privKey, hash, hashed[:])
if err != nil {
return nil, err
// If the rsaSign method got called with a non RSA private key,
// we ignore this call.
// This might happen when root is ECDSA, targets and snapshots RSA, and
// gotuf still attempts to sign root with this cryptoserver
// return nil, err
logrus.Debugf("ignoring error attempting to RSA sign with keyID: %s, %v", keyid, err)
continue
}
logrus.Debugf("appending RSA signature with Key ID: %s and method %s", privKey.ID(), method)
// Append signatures to result array
signatures = append(signatures, data.Signature{
KeyID: fingerprint,
KeyID: keyid,
Method: method,
Signature: sig[:],
})
@@ -102,7 +121,7 @@ func (ccs *RSACryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signa
}
func rsaSign(privKey *data.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) {
if strings.ToLower(privKey.Cipher()) != "rsa" {
if strings.ToUpper(privKey.Cipher()) != "RSA" {
return nil, fmt.Errorf("private key type not supported: %s", privKey.Cipher())
}
@@ -112,7 +131,7 @@ func rsaSign(privKey *data.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte,
return nil, err
}
// Use the RSA key to sign the data
// Use the RSA key to RSASSA-PSS sign the data
sig, err := rsa.SignPSS(rand.Reader, rsaPrivKey, hash, hashed[:], &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash})
if err != nil {
return nil, err
@@ -134,51 +153,77 @@ func (ccs *ECDSACryptoService) Create(role string) (*data.PublicKey, error) {
}
// Store the private key into our keystore with the name being: /GUN/ID.key
ccs.keyStore.AddKey(filepath.Join(ccs.gun, privKey.ID()), privKey)
err = ccs.keyStore.AddKey(filepath.Join(ccs.gun, privKey.ID()), privKey)
if err != nil {
return nil, fmt.Errorf("failed to add key to filestore: %v", err)
}
logrus.Debugf("generated new ECDSA key for role: %s with keyID: %s", role, privKey.ID())
return data.PublicKeyFromPrivate(*privKey), nil
}
// Sign returns the signatures for data with the given root Key ID, falling back
// if not rootKeyID is found
// Sign returns the signatures for the payload with a set of keyIDs. It ignores
// errors to sign and expects the called to validate if the number of returned
// signatures is adequate.
func (ccs *ECDSACryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signature, error) {
// Create hasher and hash data
hash := crypto.SHA256
hashed := sha256.Sum256(payload)
signatures := make([]data.Signature, 0, len(keyIDs))
for _, fingerprint := range keyIDs {
for _, keyid := range keyIDs {
// ccs.gun will be empty if this is the root key
keyName := filepath.Join(ccs.gun, fingerprint)
keyName := filepath.Join(ccs.gun, keyid)
var privKey *data.PrivateKey
var err error
// var method string
var method string
// Read PrivateKey from file
// TODO(diogo): This assumes both that only root keys are encrypted and
// that encrypted keys are always X509 encoded
if ccs.passphrase != "" {
// This is a root key
privKey, err = ccs.keyStore.GetDecryptedKey(keyName, ccs.passphrase)
// This method is added to the signature so verifiers can decode
// the X509 certificate before trying to parse the public materials
method = "ECDSA-X509"
} else {
privKey, err = ccs.keyStore.GetKey(keyName)
method = "ECDSA"
}
if err != nil {
// Note that GetDecryptedKey always fails on InitRepo.
// InitRepo gets a signer that doesn't have access to
// the root keys. Continuing here is safe because we
// end up not returning any signatures.
// TODO(diogo): figure out if there are any specific error types to
// check. We're swallowing all errors.
logrus.Debugf("Ignoring error attempting to retrieve ECDSA key ID: %s, %v", keyid, err)
continue
}
if err != nil {
fmt.Println("ERROR: ", err.Error())
}
sig, err := ecdsaSign(privKey, hash, hashed[:])
if err != nil {
return nil, err
// If the ecdsaSign method got called with a non ECDSA private key,
// we ignore this call.
// This might happen when root is RSA, targets and snapshots ECDSA, and
// gotuf still attempts to sign root with this cryptoserver
// return nil, err
logrus.Debugf("ignoring error attempting to ECDSA sign with keyID: %s, %v", privKey.ID(), err)
continue
}
logrus.Debugf("appending ECDSA signature with Key ID: %s and method: %s", privKey.ID(), method)
// Append signatures to result array
signatures = append(signatures, data.Signature{
KeyID: fingerprint,
Method: "ECDSA",
KeyID: keyid,
Method: method,
Signature: sig[:],
})
}
@@ -205,6 +250,7 @@ func ecdsaSign(privKey *data.PrivateKey, hash crypto.Hash, hashed []byte) ([]byt
rBytes, sBytes := r.Bytes(), s.Bytes()
octetLength := (ecdsaPrivKey.Params().BitSize + 7) >> 3
// MUST include leading zeros in the output
rBuf := make([]byte, octetLength-len(rBytes), octetLength)
sBuf := make([]byte, octetLength-len(sBytes), octetLength)

View File

@@ -2,7 +2,10 @@ package client
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/json"
"encoding/pem"
@@ -111,8 +114,8 @@ func NewNotaryRepository(baseDir, gun, baseURL string, rt http.RoundTripper) (*N
return nil, err
}
fmt.Println("creating non-root cryptoservice")
signer := signed.NewSigner(NewRSACryptoService(gun, privKeyStore, ""))
// TODO(diogo): This hardcodes snapshots and targets to using EC. Change it.
signer := signed.NewSigner(NewECDSACryptoService(gun, privKeyStore, ""))
nRepo := &NotaryRepository{
gun: gun,
@@ -139,7 +142,9 @@ func (r *NotaryRepository) Initialize(uSigner *UnlockedSigner) error {
return err
}
r.certificateStore.AddCert(rootCert)
rootKey := data.NewPublicKey("RSA", trustmanager.CertToPEM(rootCert))
logrus.Debugf("Adding certificate with fingerprint: %s to the certificate store.", trustmanager.FingerprintCert(rootCert))
rootKey := data.NewPublicKey(uSigner.privKey.Cipher(), trustmanager.CertToPEM(rootCert))
logrus.Debugf("Linking %s to %s.", rootKey.ID(), uSigner.ID())
err = r.rootKeyStore.Link(uSigner.ID(), rootKey.ID())
if err != nil {
return err
@@ -538,8 +543,6 @@ func (r *NotaryRepository) validateRoot(root *data.Signed) error {
// TODO(dlaw): currently assuming only one cert contained in
// public key entry. Need to fix when we want to pass in chains.
k, _ := pem.Decode([]byte(rootSigned.Keys[fingerprint].Public()))
logrus.Debug("Root PEM: ", k)
logrus.Debug("Root ID: ", fingerprint)
decodedCerts, err := x509.ParseCertificates(k.Bytes)
if err != nil {
continue
@@ -619,10 +622,21 @@ func (r *NotaryRepository) ListRootKeys() []string {
}
// GenRootKey generates a new root key protected by a given passphrase
func (r *NotaryRepository) GenRootKey(passphrase string) (string, error) {
privKey, err := trustmanager.GenerateRSAKey(rand.Reader, rsaRootKeySize)
func (r *NotaryRepository) GenRootKey(algorithm, passphrase string) (string, error) {
var err error
var privKey *data.PrivateKey
switch algorithm {
case "RSA":
privKey, err = trustmanager.GenerateRSAKey(rand.Reader, rsaRootKeySize)
case "ECDSA":
privKey, err = trustmanager.GenerateECDSAKey(rand.Reader)
default:
return "", fmt.Errorf("only RSA or ECDSA keys are currently supported. Found: %s", algorithm)
}
if err != nil {
return "", fmt.Errorf("failed to convert private key: %v", err)
return "", fmt.Errorf("failed to generate private key: %v", err)
}
r.rootKeyStore.AddEncryptedKey(privKey.ID(), privKey, passphrase)
@@ -634,16 +648,20 @@ func (r *NotaryRepository) GenRootKey(passphrase string) (string, error) {
func (r *NotaryRepository) GetRootSigner(rootKeyID, passphrase string) (*UnlockedSigner, error) {
privKey, err := r.rootKeyStore.GetDecryptedKey(rootKeyID, passphrase)
if err != nil {
return nil, fmt.Errorf("could not get decrypted root key: %v", err)
return nil, fmt.Errorf("could not get decrypted root key with keyID: %s, %v", rootKeyID, err)
}
// This signer will be used for all of the normal TUF operations, except for
// when a root key is needed.
var signer *signed.Signer
cipher := privKey.Cipher()
// Passing an empty GUN because root keys aren't associated with a GUN.
fmt.Println("creating root cryptoservice with passphrase", passphrase)
ccs := NewRSACryptoService("", r.rootKeyStore, passphrase)
signer := signed.NewSigner(ccs)
switch cipher {
case "RSA":
signer = signed.NewSigner(NewRSACryptoService("", r.rootKeyStore, passphrase))
case "ECDSA":
signer = signed.NewSigner(NewECDSACryptoService("", r.rootKeyStore, passphrase))
default:
return nil, fmt.Errorf("only RSA or ECDSA keys are currently supported. Found: %s", cipher)
}
return &UnlockedSigner{
privKey: privKey,
@@ -700,9 +718,26 @@ func (uk *UnlockedSigner) PublicKey() *data.PublicKey {
// GenerateCertificate generates an X509 Certificate from a template, given a GUN
func (uk *UnlockedSigner) GenerateCertificate(gun string) (*x509.Certificate, error) {
privKey, err := x509.ParsePKCS1PrivateKey(uk.privKey.Private())
cipher := uk.privKey.Cipher()
var publicKey crypto.PublicKey
var privateKey crypto.PrivateKey
var err error
switch cipher {
case "RSA":
var rsaPrivateKey *rsa.PrivateKey
rsaPrivateKey, err = x509.ParsePKCS1PrivateKey(uk.privKey.Private())
privateKey = rsaPrivateKey
publicKey = rsaPrivateKey.Public()
case "ECDSA":
var ecdsaPrivateKey *ecdsa.PrivateKey
ecdsaPrivateKey, err = x509.ParseECPrivateKey(uk.privKey.Private())
privateKey = ecdsaPrivateKey
publicKey = ecdsaPrivateKey.Public()
default:
return nil, fmt.Errorf("only RSA or ECDSA keys are currently supported. Found: %s", cipher)
}
if err != nil {
return nil, fmt.Errorf("failed to parse root key: %v (%s)", gun, err.Error())
return nil, fmt.Errorf("failed to parse root key: %s (%v)", gun, err)
}
template, err := trustmanager.NewCertificate(gun)
@@ -710,7 +745,7 @@ func (uk *UnlockedSigner) GenerateCertificate(gun string) (*x509.Certificate, er
return nil, fmt.Errorf("failed to create the certificate template for: %s (%v)", gun, err)
}
derBytes, err := x509.CreateCertificate(rand.Reader, template, template, privKey.Public(), privKey)
derBytes, err := x509.CreateCertificate(rand.Reader, template, template, publicKey, privateKey)
if err != nil {
return nil, fmt.Errorf("failed to create the certificate for: %s (%v)", gun, err)
}

View File

@@ -18,6 +18,8 @@ import (
)
const timestampKeyJSON = `{"keytype":"RSA","keyval":{"public":"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyyvBtTg2xzYS+MTTIBqSpI4V78tt8Yzqi7Jki/Z6NqjiDvcnbgcTqNR2t6B2W5NjGdp/hSaT2jyHM+kdmEGaPxg/zIuHbL3NIp4e0qwovWiEgACPIaELdn8O/kt5swsSKl1KMvLCH1sM86qMibNMAZ/hXOwd90TcHXCgZ91wHEAmsdjDC3dB0TT+FBgOac8RM01Y196QrZoOaDMTWh0EQfw7YbXAElhFVDFxBzDdYWbcIHSIogXQmq0CP+zaL/1WgcZZIClt2M6WCaxxF1S34wNn45gCvVZiZQ/iKWHerSr/2dGQeGo+7ezMSutRzvJ+01fInD86RS/CEtBCFZ1VyQIDAQAB","private":"MIIEpAIBAAKCAQEAyyvBtTg2xzYS+MTTIBqSpI4V78tt8Yzqi7Jki/Z6NqjiDvcnbgcTqNR2t6B2W5NjGdp/hSaT2jyHM+kdmEGaPxg/zIuHbL3NIp4e0qwovWiEgACPIaELdn8O/kt5swsSKl1KMvLCH1sM86qMibNMAZ/hXOwd90TcHXCgZ91wHEAmsdjDC3dB0TT+FBgOac8RM01Y196QrZoOaDMTWh0EQfw7YbXAElhFVDFxBzDdYWbcIHSIogXQmq0CP+zaL/1WgcZZIClt2M6WCaxxF1S34wNn45gCvVZiZQ/iKWHerSr/2dGQeGo+7ezMSutRzvJ+01fInD86RS/CEtBCFZ1VyQIDAQABAoIBAHar8FFxrE1gAGTeUpOF8fG8LIQMRwO4U6eVY7V9GpWiv6gOJTHXYFxU/aL0Ty3eQRxwy9tyVRo8EJz5pRex+e6ws1M+jLOviYqW4VocxQ8dZYd+zBvQfWmRfah7XXJ/HPUx2I05zrmR7VbGX6Bu4g5w3KnyIO61gfyQNKF2bm2Q3yblfupx3URvX0bl180R/+QN2Aslr4zxULFE6b+qJqBydrztq+AAP3WmskRxGa6irFnKxkspJqUpQN1mFselj6iQrzAcwkRPoCw0RwCCMq1/OOYvQtgxTJcO4zDVlbw54PvnxPZtcCWw7fO8oZ2Fvo2SDo75CDOATOGaT4Y9iqECgYEAzWZSpFbN9ZHmvq1lJQg//jFAyjsXRNn/nSvyLQILXltz6EHatImnXo3v+SivG91tfzBI1GfDvGUGaJpvKHoomB+qmhd8KIQhO5MBdAKZMf9fZqZofOPTD9xRXECCwdi+XqHBmL+l1OWz+O9Bh+Qobs2as/hQVgHaoXhQpE0NkTcCgYEA/Tjf6JBGl1+WxQDoGZDJrXoejzG9OFW19RjMdmPrg3t4fnbDtqTpZtCzXxPTCSeMrvplKbqAqZglWyq227ksKw4p7O6YfyhdtvC58oJmivlLr6sFaTsER7mDcYce8sQpqm+XQ8IPbnOk0Z1l6g56euTwTnew49uy25M6U1xL0P8CgYEAxEXv2Kw+OVhHV5PX4BBHHj6we88FiDyMfwM8cvfOJ0datekf9X7ImZkmZEAVPJpWBMD+B0J0jzU2b4SLjfFVkzBHVOH2Ob0xCH2MWPAWtekin7OKizUlPbW5ZV8b0+Kq30DQ/4a7D3rEhK8UPqeuX1tHZox1MAqrgbq3zJj4yvcCgYEAktYPKPm4pYCdmgFrlZ+bA0iEPf7Wvbsd91F5BtHsOOM5PQQ7e0bnvWIaEXEad/2CG9lBHlBy2WVLjDEZthILpa/h6e11ao8KwNGY0iKBuebT17rxOVMqqTjPGt8CuD2994IcEgOPFTpkAdUmyvG4XlkxbB8F6St17NPUB5DGuhsCgYA//Lfytk0FflXEeRQ16LT1YXgV7pcR2jsha4+4O5pxSFw/kTsOfJaYHg8StmROoyFnyE3sg76dCgLn0LENRCe5BvDhJnp5bMpQldG3XwcAxH8FGFNY4LtV/2ZKnJhxcONkfmzQPOmTyedOzrKQ+bNURsqLukCypP7/by6afBY4dA=="}}`
const timestampECDSAKeyJSON = `
{"keytype":"ECDSA","keyval":{"public":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgl3rzMPMEKhS1k/AX16MM4PdidpjJr+z4pj0Td+30QnpbOIARgpyR1PiFztU8BZlqG3cUazvFclr2q/xHvfrqw==","private":"MHcCAQEEIDqtcdzU7H3AbIPSQaxHl9+xYECt7NpK7B1+6ep5cv9CoAoGCCqGSM49AwEHoUQDQgAEgl3rzMPMEKhS1k/AX16MM4PdidpjJr+z4pj0Td+30QnpbOIARgpyR1PiFztU8BZlqG3cUazvFclr2q/xHvfrqw=="}}`
func createTestServer(t *testing.T) (*httptest.Server, *http.ServeMux) {
mux := http.NewServeMux()
@@ -26,7 +28,7 @@ func createTestServer(t *testing.T) (*httptest.Server, *http.ServeMux) {
mux.HandleFunc("/v2/docker.com/notary/_trust/tuf/timestamp.key", func(w http.ResponseWriter, r *http.Request) {
// Also contains the private key, but for the purpose of this
// test, we don't care
fmt.Fprint(w, timestampKeyJSON)
fmt.Fprint(w, timestampECDSAKeyJSON)
})
ts := httptest.NewServer(mux)
@@ -50,11 +52,11 @@ func TestInitRepo(t *testing.T) {
repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport)
assert.NoError(t, err, "error creating repo: %s", err)
rootKeyID, err := repo.GenRootKey("passphrase")
rootKeyID, err := repo.GenRootKey("ECDSA", "passphrase")
assert.NoError(t, err, "error generating root key: %s", err)
rootSigner, err := repo.GetRootSigner(rootKeyID, "passphrase")
assert.NoError(t, err, "error retreiving root key: %s", err)
assert.NoError(t, err, "error retrieving root key: %s", err)
err = repo.Initialize(rootSigner)
assert.NoError(t, err, "error creating repository: %s", err)
@@ -185,7 +187,7 @@ func TestAddListTarget(t *testing.T) {
repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport)
assert.NoError(t, err, "error creating repository: %s", err)
rootKeyID, err := repo.GenRootKey("passphrase")
rootKeyID, err := repo.GenRootKey("ECDSA", "passphrase")
assert.NoError(t, err, "error generating root key: %s", err)
rootSigner, err := repo.GetRootSigner(rootKeyID, "passphrase")
@@ -285,7 +287,7 @@ func TestAddListTarget(t *testing.T) {
assert.NoError(t, err, "could not apply changelist")
var tempKey data.PrivateKey
json.Unmarshal([]byte(timestampKeyJSON), &tempKey)
json.Unmarshal([]byte(timestampECDSAKeyJSON), &tempKey)
repo.privKeyStore.AddKey(filepath.Join(gun, tempKey.ID()), &tempKey)
@@ -363,7 +365,7 @@ func TestValidateRootKey(t *testing.T) {
repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport)
assert.NoError(t, err, "error creating repository: %s", err)
rootKeyID, err := repo.GenRootKey("passphrase")
rootKeyID, err := repo.GenRootKey("ECDSA", "passphrase")
assert.NoError(t, err, "error generating root key: %s", err)
rootSigner, err := repo.GetRootSigner(rootKeyID, "passphrase")

View File

@@ -116,7 +116,7 @@ func tufInit(cmd *cobra.Command, args []string) {
if err != nil {
fatalf(err.Error())
}
rootKeyID, err = nRepo.GenRootKey(passphrase)
rootKeyID, err = nRepo.GenRootKey("ECDSA", passphrase)
if err != nil {
fatalf(err.Error())
}

View File

@@ -17,6 +17,7 @@ import (
"net/url"
"time"
"github.com/Sirupsen/logrus"
"github.com/endophage/gotuf/data"
)
@@ -61,39 +62,6 @@ func CertToPEM(cert *x509.Certificate) []byte {
return pemCert
}
// KeyToPEM returns a PEM encoded key from a Private Key
func KeyToPEM(privKey *data.PrivateKey) ([]byte, error) {
if privKey.Cipher() != "RSA" {
return nil, errors.New("only RSA keys are currently supported")
}
return pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: privKey.Private()}), nil
}
// EncryptPrivateKey returns an encrypted PEM key given a Privatekey
// and a passphrase
func EncryptPrivateKey(key *data.PrivateKey, passphrase string) ([]byte, error) {
// TODO(diogo): Currently only supports RSA Private keys
if key.Cipher() != "RSA" {
return nil, errors.New("only RSA keys are currently supported")
}
password := []byte(passphrase)
cipherType := x509.PEMCipherAES256
blockType := "RSA PRIVATE KEY"
encryptedPEMBlock, err := x509.EncryptPEMBlock(rand.Reader,
blockType,
key.Private(),
password,
cipherType)
if err != nil {
return nil, err
}
return pem.EncodeToMemory(encryptedPEMBlock), nil
}
// LoadCertFromPEM returns the first certificate found in a bunch of bytes or error
// if nothing is found. Taken from https://golang.org/src/crypto/x509/cert_pool.go#L85.
func LoadCertFromPEM(pemBytes []byte) (*x509.Certificate, error) {
@@ -127,8 +95,19 @@ func fingerprintCert(cert *x509.Certificate) CertID {
block := pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}
pemdata := pem.EncodeToMemory(&block)
keyType := ""
switch cert.PublicKeyAlgorithm {
case x509.RSA:
keyType = "RSA"
case x509.ECDSA:
keyType = "ECDSA"
default:
logrus.Debug("error while fingerprinting certificate. Got Unknown key type.")
}
logrus.Debugf("certificate fingerprint of key type: %s", keyType)
// Create new TUF Key so we can compute the TUF-compliant CertID
tufKey := data.NewTUFKey("RSA", pemdata, nil)
tufKey := data.NewTUFKey(keyType, pemdata, nil)
return CertID(tufKey.ID())
}
@@ -195,6 +174,31 @@ func ParsePEMPrivateKey(pemBytes []byte, passphrase string) (*data.PrivateKey, e
}
return tufRSAPrivateKey, nil
case "EC PRIVATE KEY":
var privKeyBytes []byte
var err error
if x509.IsEncryptedPEMBlock(block) {
privKeyBytes, err = x509.DecryptPEMBlock(block, []byte(passphrase))
if err != nil {
return nil, errors.New("could not decrypt private key")
}
} else {
privKeyBytes = block.Bytes
}
rsaPrivKey, err := x509.ParseECPrivateKey(privKeyBytes)
if err != nil {
return nil, fmt.Errorf("could not parse DER encoded private key: %v", err)
}
tufRSAPrivateKey, err := ECDSAToPrivateKey(rsaPrivKey)
if err != nil {
return nil, fmt.Errorf("could not convert ecdsa.PrivateKey to data.PrivateKey: %v", err)
}
return tufRSAPrivateKey, nil
default:
return nil, fmt.Errorf("unsupported key type %q", block.Type)
}
@@ -253,6 +257,53 @@ func ECDSAToPrivateKey(ecdsaPrivKey *ecdsa.PrivateKey) (*data.PrivateKey, error)
return data.NewPrivateKey("ECDSA", ecdsaPubBytes, ecdsaPrivKeyBytes), nil
}
// KeyToPEM returns a PEM encoded key from a Private Key
func KeyToPEM(privKey *data.PrivateKey) ([]byte, error) {
var pemType string
cipher := privKey.Cipher()
switch cipher {
case "RSA":
pemType = "RSA PRIVATE KEY"
case "ECDSA":
pemType = "EC PRIVATE KEY"
default:
return nil, fmt.Errorf("only RSA or ECDSA keys are currently supported. Found: %s", cipher)
}
return pem.EncodeToMemory(&pem.Block{Type: pemType, Bytes: privKey.Private()}), nil
}
// EncryptPrivateKey returns an encrypted PEM key given a Privatekey
// and a passphrase
func EncryptPrivateKey(key *data.PrivateKey, passphrase string) ([]byte, error) {
var blockType string
cipher := key.Cipher()
switch cipher {
case "RSA":
blockType = "RSA PRIVATE KEY"
case "ECDSA":
blockType = "EC PRIVATE KEY"
default:
return nil, fmt.Errorf("only RSA or ECDSA keys are currently supported. Found: %s", cipher)
}
password := []byte(passphrase)
cipherType := x509.PEMCipherAES256
encryptedPEMBlock, err := x509.EncryptPEMBlock(rand.Reader,
blockType,
key.Private(),
password,
cipherType)
if err != nil {
return nil, err
}
return pem.EncodeToMemory(encryptedPEMBlock), nil
}
// NewCertificate returns an X509 Certificate following a template, given a GUN.
func NewCertificate(gun string) (*x509.Certificate, error) {
notBefore := time.Now()