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:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user