Starting the key refactor; rename UnlockedRootKey

Signed-off-by: Diogo Monica <diogo@docker.com>
This commit is contained in:
Diogo Monica 2015-07-08 18:01:01 -07:00
parent 2f986f1a1b
commit f9f11e5781
5 changed files with 103 additions and 81 deletions

View File

@ -39,27 +39,33 @@ 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
key, err := rsa.GenerateKey(rand.Reader, rsaKeySize)
rsaPrivKey, err := rsa.GenerateKey(rand.Reader, rsaKeySize)
if err != nil {
return nil, fmt.Errorf("could not generate private key: %v", err)
}
pemKey, err := trustmanager.KeyToPEM(key)
if err != nil {
return nil, fmt.Errorf("failed to generate the certificate for key: %v (%s)", role, err)
}
rsaPublicKey := key.PublicKey
// Using x509 to Marshal the Public key into der encoding
rsaPublicKey := rsaPrivKey.PublicKey
// 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())
ccs.keyStore.Add(privKeyFilename, pemKey)
// 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
}

View File

@ -39,28 +39,9 @@ const rsaKeySize int = 2048
/// that doesn't exist
var ErrRepositoryNotExist = errors.New("repository does not exist")
// Client is the interface that defines the Notary Client type
type Client interface {
ListPrivateKeys() []string
GenRootKey(passphrase string) (string, error)
GetRootKey(rootKeyID string, passphrase string) (UnlockedRootKey, error)
GetRepository(gun string, baseURL string, transport http.RoundTripper) (Repository, error)
}
// Repository is the interface that represents a Notary Repository
type Repository interface {
Initialize(UnlockedRootKey) error
AddTarget(target Target) error
ListTargets() ([]Target, error)
GetTargetByName(name string) (Target, error)
Publish() error
}
type UnlockedRootKey struct {
cipher string
pemPrivKey []byte
pemPubKey []byte
signer *signed.Signer
type UnlockedSigner struct {
privKey *data.PrivateKey
signer *signed.Signer
}
type NotaryClient struct {
@ -114,8 +95,7 @@ func NewClient(baseDir string) (*NotaryClient, error) {
nClient := &NotaryClient{baseDir: baseDir}
err := nClient.loadKeys(trustDir, rootKeysDir)
if err != nil {
if err := nClient.loadKeys(trustDir, rootKeysDir); err != nil {
return nil, err
}
@ -124,15 +104,13 @@ func NewClient(baseDir string) (*NotaryClient, error) {
// Initialize creates a new repository by using rootKey as the root Key for the
// TUF repository.
func (r *NotaryRepository) Initialize(uRootKey UnlockedRootKey, rootKey *data.PublicKey) error {
func (r *NotaryRepository) Initialize(uSigner *UnlockedSigner) error {
remote, err := getRemoteStore(r.Gun)
rawTSKey, err := remote.GetKey("timestamp")
if err != nil {
return err
}
fmt.Println("RawKey: ", string(rawTSKey))
parsedKey := &data.TUFKey{}
err = json.Unmarshal(rawTSKey, parsedKey)
if err != nil {
@ -140,6 +118,7 @@ func (r *NotaryRepository) Initialize(uRootKey UnlockedRootKey, rootKey *data.Pu
}
timestampKey := data.NewPublicKey(parsedKey.Cipher(), parsedKey.Public())
rootKey := uSigner.PublicKey()
targetsKey, err := r.signer.Create("targets")
if err != nil {
@ -174,21 +153,16 @@ func (r *NotaryRepository) Initialize(uRootKey UnlockedRootKey, rootKey *data.Pu
return err
}
// TODO(diogo): change to inline error catching
err = kdb.AddRole(rootRole)
if err != nil {
if err := kdb.AddRole(rootRole); err != nil {
return err
}
err = kdb.AddRole(targetsRole)
if err != nil {
if err := kdb.AddRole(targetsRole); err != nil {
return err
}
err = kdb.AddRole(snapshotRole)
if err != nil {
if err := kdb.AddRole(snapshotRole); err != nil {
return err
}
err = kdb.AddRole(timestampRole)
if err != nil {
if err := kdb.AddRole(timestampRole); err != nil {
return err
}
@ -208,7 +182,7 @@ func (r *NotaryRepository) Initialize(uRootKey UnlockedRootKey, rootKey *data.Pu
return err
}
if err := r.saveMetadata(uRootKey.signer); err != nil {
if err := r.saveMetadata(uSigner.signer); err != nil {
return err
}
@ -551,38 +525,46 @@ func (c *NotaryClient) ListPrivateKeys() []string {
// GenRootKey generates a new root key protected by a given passphrase
func (c *NotaryClient) GenRootKey(passphrase string) (string, error) {
// TODO(diogo): Refactor TUF Key creation. We should never see crypto.privatekeys
// Generates a new RSA key
key, err := rsa.GenerateKey(rand.Reader, rsaKeySize)
rsaPrivKey, err := rsa.GenerateKey(rand.Reader, rsaKeySize)
if err != nil {
return "", fmt.Errorf("could not generate private key: %v", err)
}
pemKey, err := trustmanager.KeyToPEM(key)
// 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 generate the certificate for key: %v", err)
return "", fmt.Errorf("failed to encode the private key: %v", err)
}
//
keyID := data.NewPrivateKey("RSA", pemKey, pemKey).ID()
tufPrivKey, err := trustmanager.RSAToPrivateKey(rsaPrivKey)
if err != nil {
return "", fmt.Errorf("failed to convert private key: ", err)
}
c.rootKeyStore.AddEncrypted(keyID, pemKey, passphrase)
c.rootKeyStore.AddEncrypted(tufPrivKey.ID(), pemPrivKey, passphrase)
return keyID, nil
return tufPrivKey.ID(), nil
}
// GetRootKey retreives a root key that includes the ID and a signer
func (c *NotaryClient) GetRootKey(rootKeyID, passphrase string) (UnlockedRootKey, error) {
// GetRootSigner retreives a root key that includes the ID and a signer
func (c *NotaryClient) GetRootSigner(rootKeyID, passphrase string) (*UnlockedSigner, error) {
pemPrivKey, err := c.rootKeyStore.GetDecrypted(rootKeyID, passphrase)
if err != nil {
return UnlockedRootKey{}, fmt.Errorf("could not get encrypted root key: %v", err)
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)
}
signer := signed.NewSigner(NewRootCryptoService(c.rootKeyStore, passphrase))
return UnlockedRootKey{
cipher: "RSA",
pemPrivKey: pemPrivKey,
signer: signer}, nil
return &UnlockedSigner{
privKey: tufPrivKey,
signer: signer}, nil
}
// GetRepository returns a new repository
@ -604,15 +586,15 @@ func (c *NotaryClient) GetRepository(gun string, baseURL string, transport http.
certificateStore: c.certificateStore}, nil
}
func (c *NotaryClient) InitRepository(gun string, baseURL string, transport http.RoundTripper, uRootKey UnlockedRootKey) (*NotaryRepository, error) {
func (c *NotaryClient) InitRepository(gun string, baseURL string, transport http.RoundTripper, uSigner *UnlockedSigner) (*NotaryRepository, error) {
// Creates and saves a trusted certificate for this store, with this root key
rootCert, err := uRootKey.GenerateCertificate(gun)
rootCert, err := uSigner.GenerateCertificate(gun)
if err != nil {
return nil, err
}
c.certificateStore.AddCert(rootCert)
rootKey := data.NewPublicKey("RSA", trustmanager.CertToPEM(rootCert))
err = c.rootKeyStore.Link(uRootKey.ID(), rootKey.ID())
err = c.rootKeyStore.Link(uSigner.ID(), rootKey.ID())
if err != nil {
return nil, err
}
@ -633,7 +615,7 @@ func (c *NotaryClient) InitRepository(gun string, baseURL string, transport http
caStore: c.caStore,
certificateStore: c.certificateStore}
err = nRepo.Initialize(uRootKey, rootKey)
err = nRepo.Initialize(uSigner)
if err != nil {
return nil, err
}
@ -679,14 +661,18 @@ func (c *NotaryClient) loadKeys(trustDir, rootKeysDir string) error {
}
// ID gets a consistent ID based on the PrivateKey bytes and cipher type
func (uk *UnlockedRootKey) ID() string {
return data.NewPrivateKey(uk.cipher, uk.pemPrivKey, uk.pemPrivKey).ID()
func (uk *UnlockedSigner) ID() string {
return uk.PublicKey().ID()
}
// PublicKey Returns the public key associated with the Root Key
func (uk *UnlockedSigner) PublicKey() *data.PublicKey {
return data.PublicKeyFromPrivate(*uk.privKey)
}
// GenerateCertificate
func (uk *UnlockedRootKey) GenerateCertificate(gun string) (*x509.Certificate, error) {
privKeyBytes, _ := pem.Decode(uk.pemPrivKey)
privKey, err := x509.ParsePKCS1PrivateKey(privKeyBytes.Bytes)
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())
}

View File

@ -26,11 +26,11 @@ func TestInitRepo(t *testing.T) {
rootKeyID, err := client.GenRootKey("passphrase")
assert.NoError(t, err, "error generating root key: %s", err)
rootKey, err := client.GetRootKey(rootKeyID, "passphrase")
rootSigner, err := client.GetRootSigner(rootKeyID, "passphrase")
assert.NoError(t, err, "error retreiving root key: %s", err)
gun := "docker.com/notary"
repo, err := client.InitRepository(gun, "", nil, rootKey)
repo, err := client.InitRepository(gun, "", nil, rootSigner)
assert.NoError(t, err, "error creating repository: %s", err)
// Inspect contents of the temporary directory
@ -61,7 +61,7 @@ func TestInitRepo(t *testing.T) {
// Look for keys in root_keys
// There should be a file named after the key ID of the root key we
// passed in.
rootKeyFilename := rootKey.ID() + ".key"
rootKeyFilename := rootSigner.ID() + ".key"
_, err = os.Stat(filepath.Join(tempBaseDir, "private", "root_keys", rootKeyFilename))
assert.NoError(t, err, "missing root key")

View File

@ -106,12 +106,12 @@ func tufInit(cmd *cobra.Command, args []string) {
if err != nil {
fatalf(err.Error())
}
rootKey, err := nClient.GetRootKey(rootKeyID, "passphrase")
rootSigner, err := nClient.GetRootSigner(rootKeyID, "passphrase")
if err != nil {
fatalf(err.Error())
}
_, err = nClient.InitRepository(args[0], "", t, rootKey)
_, err = nClient.InitRepository(args[0], "", t, rootSigner)
if err != nil {
fatalf(err.Error())
}

View File

@ -120,14 +120,6 @@ func loadCertFromPEM(pemBytes []byte) (*x509.Certificate, error) {
return nil, errors.New("no certificates found in PEM data")
}
func FingerprintPEMCert(pemCert []byte) (string, error) {
cert, err := loadCertFromPEM(pemCert)
if err != nil {
return "", err
}
return FingerprintCert(cert), nil
}
// FingerprintCert returns a TUF compliant fingerprint for a X509 Certificate
func FingerprintCert(cert *x509.Certificate) string {
return string(fingerprintCert(cert))
@ -202,6 +194,44 @@ func ParsePEMPrivateKey(pemBytes []byte) (crypto.PrivateKey, 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")
}
switch block.Type {
case "RSA PRIVATE KEY":
rsaPrivKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("could not parse PEM: %v", err)
}
tufRSAPrivateKey, err := RSAToPrivateKey(rsaPrivKey)
if err != nil {
return nil, fmt.Errorf("could not convert crypto.PrivateKey to PrivateKey: %v", err)
}
return tufRSAPrivateKey, nil
default:
return nil, fmt.Errorf("unsupported key type %q", block.Type)
}
}
func RSAToPrivateKey(rsaPrivKey *rsa.PrivateKey) (*data.PrivateKey, error) {
// Get a DER-encoded representation of the PublicKey
rsaPubBytes, err := x509.MarshalPKIXPublicKey(&rsaPrivKey.PublicKey)
if err != nil {
return nil, fmt.Errorf("failed to marshal private key: %v", err)
}
// Get a DER-encoded representation of the PrivateKey
rsaPrivBytes := x509.MarshalPKCS1PrivateKey(rsaPrivKey)
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) {