mirror of https://github.com/docker/docs.git
Major refactor of keys
Signed-off-by: Diogo Monica <diogo@docker.com>
This commit is contained in:
parent
73ca456297
commit
4635bed2db
|
@ -6,7 +6,6 @@ import (
|
|||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
@ -38,36 +37,15 @@ func NewRootCryptoService(rootKeyStore *trustmanager.KeyFileStore, passphrase st
|
|||
|
||||
// Create is used to generate keys for targets, snapshots and timestamps
|
||||
func (ccs *CryptoService) Create(role string) (*data.PublicKey, error) {
|
||||
// Generates a new RSA key
|
||||
rsaPrivKey, err := rsa.GenerateKey(rand.Reader, rsaKeySize)
|
||||
privKey, err := trustmanager.GenerateRSAKey(rand.Reader, rsaKeySize)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not generate private key: %v", err)
|
||||
return nil, fmt.Errorf("failed to generate RSA key: %v", err)
|
||||
}
|
||||
|
||||
rsaPublicKey := rsaPrivKey.PublicKey
|
||||
// Store the private key into our keystore with the name being: /GUN/ID.key
|
||||
ccs.keyStore.AddKey(filepath.Join(ccs.gun, privKey.ID()), privKey)
|
||||
|
||||
// Using x509 to Marshal the Public key into DER encoding
|
||||
pubBytes, err := x509.MarshalPKIXPublicKey(&rsaPublicKey)
|
||||
if err != nil {
|
||||
return nil, errors.New("Failed to Marshal public key.")
|
||||
}
|
||||
|
||||
tufKey := data.NewPublicKey("RSA", pubBytes)
|
||||
|
||||
// Passing in the the GUN + keyID as the name for the private key and adding it
|
||||
// to our KeyFileStore. Final storage will be under $BASE_PATH/GUN/keyID.key
|
||||
privKeyFilename := filepath.Join(ccs.gun, tufKey.ID())
|
||||
|
||||
// Get a PEM encoded representation of the private key
|
||||
pemRSAPrivKey, err := trustmanager.KeyToPEM(rsaPrivKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate the certificate for key: %v (%s)", role, err)
|
||||
}
|
||||
|
||||
// Store the PEM-encoded private key into our keystore
|
||||
ccs.keyStore.Add(privKeyFilename, pemRSAPrivKey)
|
||||
|
||||
return tufKey, nil
|
||||
return data.PublicKeyFromPrivate(*privKey), nil
|
||||
}
|
||||
|
||||
// Sign returns the signatures for data with the given keyIDs
|
||||
|
@ -81,29 +59,20 @@ func (ccs *CryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signatur
|
|||
// Get the PrivateKey filename
|
||||
privKeyFilename := filepath.Join(ccs.gun, fingerprint)
|
||||
// Read PrivateKey from file
|
||||
privPEMBytes, err := ccs.keyStore.Get(privKeyFilename)
|
||||
privKey, err := ccs.keyStore.GetKey(privKeyFilename)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse PrivateKey
|
||||
privKeyBytes, _ := pem.Decode(privPEMBytes)
|
||||
privKey, err := x509.ParsePKCS1PrivateKey(privKeyBytes.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Sign the data
|
||||
sig, err := rsa.SignPKCS1v15(rand.Reader, privKey, hash, hashed[:])
|
||||
sig, err := sign(privKey, hash, hashed[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Append signatures to result array
|
||||
signatures = append(signatures, data.Signature{
|
||||
KeyID: fingerprint,
|
||||
Method: "RSA",
|
||||
//Method: "RSASSA-PKCS1-V1_5-SIGN",
|
||||
KeyID: fingerprint,
|
||||
Method: "RSA",
|
||||
Signature: sig[:],
|
||||
})
|
||||
}
|
||||
|
@ -126,21 +95,13 @@ func (ccs *RootCryptoService) Sign(keyIDs []string, payload []byte) ([]data.Sign
|
|||
signatures := make([]data.Signature, 0, len(keyIDs))
|
||||
for _, fingerprint := range keyIDs {
|
||||
// Read PrivateKey from file
|
||||
privPEMBytes, err := ccs.rootKeyStore.GetDecrypted(fingerprint, ccs.passphrase)
|
||||
privKey, err := ccs.rootKeyStore.GetDecryptedKey(fingerprint, ccs.passphrase)
|
||||
if err != nil {
|
||||
// TODO(diogo): This error should be returned to the user in someway
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse PrivateKey
|
||||
privKeyBytes, _ := pem.Decode(privPEMBytes)
|
||||
privKey, err := x509.ParsePKCS1PrivateKey(privKeyBytes.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Sign the data
|
||||
sig, err := rsa.SignPKCS1v15(rand.Reader, privKey, hash, hashed[:])
|
||||
sig, err := sign(privKey, hash, hashed[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -152,5 +113,27 @@ func (ccs *RootCryptoService) Sign(keyIDs []string, payload []byte) ([]data.Sign
|
|||
Signature: sig[:],
|
||||
})
|
||||
}
|
||||
|
||||
return signatures, nil
|
||||
}
|
||||
|
||||
func sign(privKey *data.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) {
|
||||
// TODO(diogo): Implement support for ECDSA.
|
||||
if privKey.Cipher() != "RSA" {
|
||||
return nil, fmt.Errorf("private key type not supported: %s", privKey.Cipher())
|
||||
}
|
||||
|
||||
// Create an rsa.PrivateKey out of the private key bytes
|
||||
rsaPrivKey, err := x509.ParsePKCS1PrivateKey(privKey.Private())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Use the RSA key to sign the data
|
||||
sig, err := rsa.SignPKCS1v15(rand.Reader, rsaPrivKey, hash, hashed[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sig, nil
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package client
|
|||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
|
@ -58,12 +57,12 @@ type NotaryRepository struct {
|
|||
baseURL string
|
||||
tufRepoPath string
|
||||
transport http.RoundTripper
|
||||
signer *signed.Signer
|
||||
tufRepo *tuf.TufRepo
|
||||
fileStore store.MetadataStore
|
||||
privKeyStore *trustmanager.KeyFileStore
|
||||
caStore trustmanager.X509Store
|
||||
certificateStore trustmanager.X509Store
|
||||
fileStore store.MetadataStore
|
||||
signer *signed.Signer
|
||||
tufRepo *tuf.TufRepo
|
||||
privKeyStore *trustmanager.KeyFileStore
|
||||
rootKeyStore *trustmanager.KeyFileStore
|
||||
rootSigner *UnlockedSigner
|
||||
}
|
||||
|
@ -609,45 +608,29 @@ func (c *NotaryRepository) ListPrivateKeys() []string {
|
|||
|
||||
// GenRootKey generates a new root key protected by a given passphrase
|
||||
func (c *NotaryRepository) GenRootKey(passphrase string) (string, error) {
|
||||
// TODO(diogo): Refactor TUF Key creation. We should never see crypto.privatekeys
|
||||
// Generates a new RSA key
|
||||
rsaPrivKey, err := rsa.GenerateKey(rand.Reader, rsaKeySize)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not generate private key: %v", err)
|
||||
}
|
||||
|
||||
// Encode the private key in PEM format since that is the final storage format
|
||||
pemPrivKey, err := trustmanager.KeyToPEM(rsaPrivKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to encode the private key: %v", err)
|
||||
}
|
||||
|
||||
tufPrivKey, err := trustmanager.RSAToPrivateKey(rsaPrivKey)
|
||||
privKey, err := trustmanager.GenerateRSAKey(rand.Reader, rsaKeySize)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to convert private key: ", err)
|
||||
}
|
||||
|
||||
c.rootKeyStore.AddEncrypted(tufPrivKey.ID(), pemPrivKey, passphrase)
|
||||
c.rootKeyStore.AddEncryptedKey(privKey.ID(), privKey, passphrase)
|
||||
|
||||
return tufPrivKey.ID(), nil
|
||||
return privKey.ID(), nil
|
||||
}
|
||||
|
||||
// GetRootSigner retreives a root key that includes the ID and a signer
|
||||
func (c *NotaryRepository) GetRootSigner(rootKeyID, passphrase string) (*UnlockedSigner, error) {
|
||||
pemPrivKey, err := c.rootKeyStore.GetDecrypted(rootKeyID, passphrase)
|
||||
privKey, err := c.rootKeyStore.GetDecryptedKey(rootKeyID, passphrase)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get decrypted root key: %v", err)
|
||||
}
|
||||
|
||||
tufPrivKey, err := trustmanager.TufParsePEMPrivateKey(pemPrivKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get parse root key: %v", err)
|
||||
}
|
||||
|
||||
// This signer will be used for all of the normal TUF operations, except for
|
||||
// when a root key is needed.
|
||||
signer := signed.NewSigner(NewRootCryptoService(c.rootKeyStore, passphrase))
|
||||
|
||||
return &UnlockedSigner{
|
||||
privKey: tufPrivKey,
|
||||
privKey: privKey,
|
||||
signer: signer}, nil
|
||||
}
|
||||
|
||||
|
@ -676,6 +659,7 @@ func (c *NotaryRepository) loadKeys(trustDir, rootKeysDir string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Load the keystore that will hold all of our encrypted Root Private Keys
|
||||
rootKeyStore, err := trustmanager.NewKeyFileStore(rootKeysDir)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -693,29 +677,32 @@ func (uk *UnlockedSigner) ID() string {
|
|||
return uk.PublicKey().ID()
|
||||
}
|
||||
|
||||
// PublicKey Returns the public key associated with the Root Key
|
||||
// PublicKey Returns the public key associated with the Private Key within the Signer
|
||||
func (uk *UnlockedSigner) PublicKey() *data.PublicKey {
|
||||
return data.PublicKeyFromPrivate(*uk.privKey)
|
||||
}
|
||||
|
||||
// GenerateCertificate
|
||||
// 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())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse root key: %v (%s)", gun, err.Error())
|
||||
}
|
||||
|
||||
//TODO (diogo): We're hardcoding the Organization to be the GUN. Probably want to change it
|
||||
template := trustmanager.NewCertificate(gun, gun)
|
||||
template, err := trustmanager.NewCertificate(gun)
|
||||
if err != nil {
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate the certificate for: %v (%s)", gun, err.Error())
|
||||
return nil, fmt.Errorf("failed to create the certificate for: %s (%v)", gun, err)
|
||||
}
|
||||
|
||||
// Encode the new certificate into PEM
|
||||
cert, err := x509.ParseCertificate(derBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse the certificate for key: %v (%s)", gun, err.Error())
|
||||
return nil, fmt.Errorf("failed to parse the certificate for key: %s (%v)", gun, err)
|
||||
}
|
||||
|
||||
return cert, nil
|
||||
|
|
|
@ -24,8 +24,8 @@ type FileStore interface {
|
|||
|
||||
type EncryptedFileStore interface {
|
||||
FileStore
|
||||
AddEncrypted(fileName string, keyBytes []byte, passphrase string) error
|
||||
GetDecrypted(fileName string, passphrase string) ([]byte, error)
|
||||
AddEncrypted(fileName string, data []byte, passphrase string) error
|
||||
GetDecrypted(fileName, passphrase string) ([]byte, error)
|
||||
}
|
||||
|
||||
// SimpleFileStore implements FileStore
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package trustmanager
|
||||
|
||||
import "github.com/endophage/gotuf/data"
|
||||
|
||||
const (
|
||||
keyExtension = "key"
|
||||
)
|
||||
|
@ -20,39 +22,55 @@ func NewKeyFileStore(baseDir string) (*KeyFileStore, error) {
|
|||
return &KeyFileStore{fileStore}, nil
|
||||
}
|
||||
|
||||
// AddKey stores the contents of a PEM-encoded private key as a PEM block
|
||||
func (s *KeyFileStore) AddKey(name string, privKey *data.PrivateKey) error {
|
||||
pemPrivKey, err := KeyToPEM(privKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.Add(name, pemPrivKey)
|
||||
}
|
||||
|
||||
// GetKey returns the PrivateKey given a KeyID
|
||||
func (s *KeyFileStore) GetKey(name string) (*data.PrivateKey, error) {
|
||||
keyBytes, err := s.Get(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert PEM encoded bytes back to a PrivateKey
|
||||
privKey, err := ParsePEMPrivateKey(keyBytes, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return privKey, nil
|
||||
}
|
||||
|
||||
// AddEncrypted stores the contents of a PEM-encoded private key as an encrypted PEM block
|
||||
func (s *KeyFileStore) AddEncrypted(fileName string, pemKey []byte, passphrase string) error {
|
||||
|
||||
privKey, err := ParsePEMPrivateKey(pemKey)
|
||||
func (s *KeyFileStore) AddEncryptedKey(name string, privKey *data.PrivateKey, passphrase string) error {
|
||||
encryptedPrivKey, err := EncryptPrivateKey(privKey, passphrase)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
encryptedKey, err := EncryptPrivateKey(privKey, passphrase)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.Add(fileName, encryptedKey)
|
||||
return s.Add(name, encryptedPrivKey)
|
||||
}
|
||||
|
||||
// GetDecrypted decrypts and returns the PEM Encoded private key given a flename
|
||||
// and a passphrase
|
||||
func (s *KeyFileStore) GetDecrypted(fileName string, passphrase string) ([]byte, error) {
|
||||
keyBytes, err := s.Get(fileName)
|
||||
func (s *KeyFileStore) GetDecryptedKey(name string, passphrase string) (*data.PrivateKey, error) {
|
||||
keyBytes, err := s.Get(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Gets an unencrypted PrivateKey.
|
||||
privKey, err := ParsePEMEncryptedPrivateKey(keyBytes, passphrase)
|
||||
privKey, err := ParsePEMPrivateKey(keyBytes, passphrase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return KeyToPEM(privKey)
|
||||
}
|
||||
|
||||
func (s *KeyFileStore) Link(src, dst string) error {
|
||||
return s.FileStore.Link(src, dst)
|
||||
return privKey, nil
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package trustmanager
|
|||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -30,19 +29,13 @@ func TestAddKey(t *testing.T) {
|
|||
t.Fatalf("failed to create new key filestore: %v", err)
|
||||
}
|
||||
|
||||
key, err := rsa.GenerateKey(rand.Reader, 1024)
|
||||
privKey, err := GenerateRSAKey(rand.Reader, 512)
|
||||
if err != nil {
|
||||
t.Fatalf("could not generate private key: %v", err)
|
||||
}
|
||||
|
||||
// Get the PEM for the key
|
||||
pemKey, err := KeyToPEM(key)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to convert private key to PEM: %v", err)
|
||||
}
|
||||
|
||||
// Call the Add function
|
||||
err = store.Add(testName, pemKey)
|
||||
// Call the AddKey function
|
||||
err = store.AddKey(testName, privKey)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to add file to store: %v", err)
|
||||
}
|
||||
|
@ -111,19 +104,23 @@ EMl3eFOJXjIch/wIesRSN+2dGOsl7neercjMh1i9RvpCwHDx/E0=
|
|||
t.Fatalf("failed to create new key filestore: %v", err)
|
||||
}
|
||||
|
||||
// Call the Get function
|
||||
pemKey, err := store.Get(testName)
|
||||
// Call the GetKey function
|
||||
privKey, err := store.GetKey(testName)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get file from store: %v", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(testData, pemKey) {
|
||||
pemPrivKey, err := KeyToPEM(privKey)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to convert key to PEM: %v", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(testData, pemPrivKey) {
|
||||
t.Fatalf("unexpected content in the file: %s", filePath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddEncryptedAndGetDecrypted(t *testing.T) {
|
||||
testName := "docker.com/notary/root"
|
||||
testExt := "key"
|
||||
|
||||
// Temporary directory where test files will be created
|
||||
|
@ -132,9 +129,6 @@ func TestAddEncryptedAndGetDecrypted(t *testing.T) {
|
|||
t.Fatalf("failed to create a temporary directory: %v", err)
|
||||
}
|
||||
|
||||
// Since we're generating this manually we need to add the extension '.'
|
||||
expectedFilePath := filepath.Join(tempBaseDir, testName+"."+testExt)
|
||||
|
||||
// Create our FileStore
|
||||
store, err := NewKeyFileStore(tempBaseDir)
|
||||
if err != nil {
|
||||
|
@ -142,35 +136,38 @@ func TestAddEncryptedAndGetDecrypted(t *testing.T) {
|
|||
}
|
||||
|
||||
// Generate new PrivateKey
|
||||
key, err := rsa.GenerateKey(rand.Reader, 1024)
|
||||
privKey, err := GenerateRSAKey(rand.Reader, 512)
|
||||
if err != nil {
|
||||
t.Fatalf("could not generate private key: %v", err)
|
||||
}
|
||||
|
||||
// Get PEM encodedd key
|
||||
pemKey, err := KeyToPEM(key)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not encode key to PEM: %v", err)
|
||||
}
|
||||
|
||||
// Call the Add function
|
||||
err = store.AddEncrypted(testName, pemKey, "diogomonica")
|
||||
// Call the AddEncryptedKey function
|
||||
err = store.AddEncryptedKey(privKey.ID(), privKey, "diogomonica")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to add file to store: %v", err)
|
||||
}
|
||||
|
||||
pemPrivKey, err := store.GetDecrypted(testName, "diogomonica")
|
||||
// Since we're generating this manually we need to add the extension '.'
|
||||
expectedFilePath := filepath.Join(tempBaseDir, privKey.ID()+"."+testExt)
|
||||
|
||||
// Check to see if file exists
|
||||
_, err = ioutil.ReadFile(expectedFilePath)
|
||||
if err != nil {
|
||||
t.Fatalf("expected file not found: %v", err)
|
||||
}
|
||||
|
||||
// Call the GetDecryptedKey function
|
||||
readPrivKey, err := store.GetDecryptedKey(privKey.ID(), "diogomonica")
|
||||
if err != nil {
|
||||
t.Fatalf("could not decrypt private key: %v", err)
|
||||
}
|
||||
|
||||
if !strings.Contains(string(pemKey), string(pemPrivKey)) {
|
||||
t.Fatalf("expected private key content in the file: %s", expectedFilePath)
|
||||
if !bytes.Equal(privKey.Private(), readPrivKey.Private()) {
|
||||
t.Fatalf("written key and loaded key do not match")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDecryptedWithTamperedCipherText(t *testing.T) {
|
||||
testName := "docker.com/notary/root"
|
||||
testExt := "key"
|
||||
|
||||
// Temporary directory where test files will be created
|
||||
|
@ -179,9 +176,6 @@ func TestGetDecryptedWithTamperedCipherText(t *testing.T) {
|
|||
t.Fatalf("failed to create a temporary directory: %v", err)
|
||||
}
|
||||
|
||||
// Since we're generating this manually we need to add the extension '.'
|
||||
expectedFilePath := filepath.Join(tempBaseDir, testName+"."+testExt)
|
||||
|
||||
// Create our FileStore
|
||||
store, err := NewKeyFileStore(tempBaseDir)
|
||||
if err != nil {
|
||||
|
@ -189,24 +183,22 @@ func TestGetDecryptedWithTamperedCipherText(t *testing.T) {
|
|||
}
|
||||
|
||||
// Generate a new Private Key
|
||||
key, err := rsa.GenerateKey(rand.Reader, 1024)
|
||||
privKey, err := GenerateRSAKey(rand.Reader, 512)
|
||||
if err != nil {
|
||||
t.Fatalf("could not generate private key: %v", err)
|
||||
}
|
||||
|
||||
// Get PEM encodedd key
|
||||
pemKey, err := KeyToPEM(key)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not encode key to PEM: %v", err)
|
||||
}
|
||||
// Call the Add function
|
||||
err = store.AddEncrypted(testName, pemKey, "diogomonica")
|
||||
// Call the AddEncryptedKey function
|
||||
err = store.AddEncryptedKey(privKey.ID(), privKey, "diogomonica")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to add file to store: %v", err)
|
||||
}
|
||||
|
||||
// Since we're generating this manually we need to add the extension '.'
|
||||
expectedFilePath := filepath.Join(tempBaseDir, privKey.ID()+"."+testExt)
|
||||
|
||||
// Get file description, open file
|
||||
fp, _ := os.OpenFile(expectedFilePath, os.O_WRONLY, 0600)
|
||||
fp, err := os.OpenFile(expectedFilePath, os.O_WRONLY, 0600)
|
||||
if err != nil {
|
||||
t.Fatalf("expected file not found: %v", err)
|
||||
}
|
||||
|
@ -215,7 +207,7 @@ func TestGetDecryptedWithTamperedCipherText(t *testing.T) {
|
|||
fp.WriteAt([]byte("a"), int64(1))
|
||||
|
||||
// Try to decrypt the file
|
||||
_, err = store.GetDecrypted(testName, "diogomonica")
|
||||
_, err = store.GetDecryptedKey(privKey.ID(), "diogomonica")
|
||||
if err == nil {
|
||||
t.Fatalf("expected error while decrypting the content due to invalid cipher text")
|
||||
}
|
||||
|
@ -237,24 +229,19 @@ func TestGetDecryptedWithInvalidPassphrase(t *testing.T) {
|
|||
}
|
||||
|
||||
// Generate a new random RSA Key
|
||||
key, err := rsa.GenerateKey(rand.Reader, 1024)
|
||||
privKey, err := GenerateRSAKey(rand.Reader, 512)
|
||||
if err != nil {
|
||||
t.Fatalf("could not generate private key: %v", err)
|
||||
}
|
||||
|
||||
// Get PEM encodedd key
|
||||
pemKey, err := KeyToPEM(key)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not encode key to PEM: %v", err)
|
||||
}
|
||||
// Call the Add function
|
||||
err = store.AddEncrypted(testName, pemKey, "diogomonica")
|
||||
// Call the AddEncryptedKey function
|
||||
err = store.AddEncryptedKey(privKey.ID(), privKey, "diogomonica")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to add file to stoAFre: %v", err)
|
||||
}
|
||||
|
||||
// Try to decrypt the file with an invalid passphrase
|
||||
_, err = store.GetDecrypted(testName, "diegomonica")
|
||||
_, err = store.GetDecryptedKey(testName, "diegomonica")
|
||||
if err == nil {
|
||||
t.Fatalf("expected error while decrypting the content due to invalid passphrase")
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package trustmanager
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
|
@ -9,6 +8,7 @@ import (
|
|||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"net/http"
|
||||
|
@ -59,34 +59,30 @@ func CertToPEM(cert *x509.Certificate) []byte {
|
|||
return pemCert
|
||||
}
|
||||
|
||||
// KeyToPEM returns a PEM encoded key from a crypto.PrivateKey
|
||||
func KeyToPEM(key crypto.PrivateKey) ([]byte, error) {
|
||||
rsaKey, ok := key.(*rsa.PrivateKey)
|
||||
if !ok {
|
||||
// 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")
|
||||
}
|
||||
|
||||
keyBytes := x509.MarshalPKCS1PrivateKey(rsaKey)
|
||||
return pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: keyBytes}), nil
|
||||
return pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: privKey.Private()}), nil
|
||||
}
|
||||
|
||||
// EncryptPrivateKey returns an encrypted PEM encoded key given a Private key
|
||||
// EncryptPrivateKey returns an encrypted PEM key given a Privatekey
|
||||
// and a passphrase
|
||||
func EncryptPrivateKey(key crypto.PrivateKey, passphrase string) ([]byte, error) {
|
||||
rsaKey, ok := key.(*rsa.PrivateKey)
|
||||
if !ok {
|
||||
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")
|
||||
}
|
||||
|
||||
keyBytes := x509.MarshalPKCS1PrivateKey(rsaKey)
|
||||
|
||||
password := []byte(passphrase)
|
||||
cipherType := x509.PEMCipherAES256
|
||||
blockType := "RSA PRIVATE KEY"
|
||||
|
||||
encryptedPEMBlock, err := x509.EncryptPEMBlock(rand.Reader,
|
||||
blockType,
|
||||
keyBytes,
|
||||
key.Private(),
|
||||
password,
|
||||
cipherType)
|
||||
if err != nil {
|
||||
|
@ -164,61 +160,55 @@ func LoadCertFromFile(filename string) (*x509.Certificate, error) {
|
|||
return nil, errors.New("could not load certificate from file")
|
||||
}
|
||||
|
||||
// LoadKeyFromFile returns a PrivateKey given a filename
|
||||
func LoadKeyFromFile(filename string) (crypto.PrivateKey, error) {
|
||||
pemBytes, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key, err := ParsePEMPrivateKey(pemBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// ParsePEMPrivateKey returns a private key from a PEM encoded private key. It
|
||||
// only supports RSA (PKCS#1).
|
||||
func ParsePEMPrivateKey(pemBytes []byte) (crypto.PrivateKey, error) {
|
||||
// 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 key found")
|
||||
return nil, errors.New("no valid private key found")
|
||||
}
|
||||
|
||||
switch block.Type {
|
||||
case "RSA PRIVATE KEY":
|
||||
return x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported key type %q", block.Type)
|
||||
}
|
||||
}
|
||||
var privKeyBytes []byte
|
||||
var err error
|
||||
|
||||
// TufParsePEMPrivateKey returns a data.PrivateKey from a PEM encoded private key. It
|
||||
// only supports RSA (PKCS#1).
|
||||
func TufParsePEMPrivateKey(pemBytes []byte) (*data.PrivateKey, error) {
|
||||
block, _ := pem.Decode(pemBytes)
|
||||
if block == nil {
|
||||
return nil, errors.New("no valid key found")
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
switch block.Type {
|
||||
case "RSA PRIVATE KEY":
|
||||
rsaPrivKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
rsaPrivKey, err := x509.ParsePKCS1PrivateKey(privKeyBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse PEM: %v", err)
|
||||
return nil, fmt.Errorf("could not parse DER encoded key: %v", err)
|
||||
}
|
||||
|
||||
tufRSAPrivateKey, err := RSAToPrivateKey(rsaPrivKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not convert crypto.PrivateKey to PrivateKey: %v", err)
|
||||
return nil, fmt.Errorf("could not convert rsa.PrivateKey to data.PrivateKey: %v", err)
|
||||
}
|
||||
|
||||
return tufRSAPrivateKey, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported key type %q", block.Type)
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateRSAKey generates an RSA Private key and returns a TUF PrivateKey
|
||||
func GenerateRSAKey(random io.Reader, bits int) (*data.PrivateKey, error) {
|
||||
rsaPrivKey, err := rsa.GenerateKey(random, bits)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not generate private key: %v", err)
|
||||
}
|
||||
|
||||
return RSAToPrivateKey(rsaPrivKey)
|
||||
}
|
||||
|
||||
// RSAToPrivateKey converts an rsa.Private key to a TUF data.PrivateKey type
|
||||
func RSAToPrivateKey(rsaPrivKey *rsa.PrivateKey) (*data.PrivateKey, error) {
|
||||
// Get a DER-encoded representation of the PublicKey
|
||||
rsaPubBytes, err := x509.MarshalPKIXPublicKey(&rsaPrivKey.PublicKey)
|
||||
|
@ -232,43 +222,23 @@ func RSAToPrivateKey(rsaPrivKey *rsa.PrivateKey) (*data.PrivateKey, error) {
|
|||
return data.NewPrivateKey("RSA", rsaPubBytes, rsaPrivBytes), nil
|
||||
}
|
||||
|
||||
// ParsePEMEncryptedPrivateKey returns a private key from a PEM encrypted private key. It
|
||||
// only supports RSA (PKCS#1).
|
||||
func ParsePEMEncryptedPrivateKey(pemBytes []byte, passphrase string) (crypto.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":
|
||||
if !x509.IsEncryptedPEMBlock(block) {
|
||||
return nil, errors.New("private key is not encrypted")
|
||||
}
|
||||
|
||||
decryptedPEMBlock, err := x509.DecryptPEMBlock(block, []byte(passphrase))
|
||||
if err != nil {
|
||||
return nil, errors.New("could not decrypt private key")
|
||||
}
|
||||
|
||||
return x509.ParsePKCS1PrivateKey(decryptedPEMBlock)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported key type %q", block.Type)
|
||||
}
|
||||
}
|
||||
|
||||
func NewCertificate(gun, organization string) *x509.Certificate {
|
||||
// NewCertificate returns an X509 Certificate following a template, given a GUN.
|
||||
func NewCertificate(gun string) (*x509.Certificate, error) {
|
||||
notBefore := time.Now()
|
||||
notAfter := notBefore.Add(time.Hour * 24 * 365 * 2)
|
||||
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
// TODO(diogo): Don't silently ignore this error
|
||||
serialNumber, _ := rand.Int(rand.Reader, serialNumberLimit)
|
||||
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate new certificate: %v", err)
|
||||
}
|
||||
|
||||
// TODO(diogo): Currently hard coding organization to be the gun. Revisit.
|
||||
return &x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{organization},
|
||||
Organization: []string{gun},
|
||||
CommonName: gun,
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
|
@ -277,5 +247,5 @@ func NewCertificate(gun, organization string) *x509.Certificate {
|
|||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning},
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue