Major refactor of keys

Signed-off-by: Diogo Monica <diogo@docker.com>
This commit is contained in:
Diogo Monica 2015-07-09 01:57:19 -07:00
parent 73ca456297
commit 4635bed2db
6 changed files with 179 additions and 234 deletions

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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")
}

View File

@ -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
}