From f9f11e57815655e369aab390741181793e5927d4 Mon Sep 17 00:00:00 2001 From: Diogo Monica Date: Wed, 8 Jul 2015 18:01:01 -0700 Subject: [PATCH] Starting the key refactor; rename UnlockedRootKey Signed-off-by: Diogo Monica --- client/cli_crypto_service.go | 22 +++++--- client/client.go | 106 +++++++++++++++-------------------- client/client_test.go | 6 +- cmd/notary/tuf.go | 4 +- trustmanager/x509utils.go | 46 ++++++++++++--- 5 files changed, 103 insertions(+), 81 deletions(-) diff --git a/client/cli_crypto_service.go b/client/cli_crypto_service.go index 4fde462d85..ea47d59395 100644 --- a/client/cli_crypto_service.go +++ b/client/cli_crypto_service.go @@ -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 } diff --git a/client/client.go b/client/client.go index e149d02644..98523a3ecd 100644 --- a/client/client.go +++ b/client/client.go @@ -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()) } diff --git a/client/client_test.go b/client/client_test.go index 37e7785130..c2526a9810 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -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") diff --git a/cmd/notary/tuf.go b/cmd/notary/tuf.go index d51c778502..1a49b87356 100644 --- a/cmd/notary/tuf.go +++ b/cmd/notary/tuf.go @@ -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()) } diff --git a/trustmanager/x509utils.go b/trustmanager/x509utils.go index fe5099cfe6..563342f65a 100644 --- a/trustmanager/x509utils.go +++ b/trustmanager/x509utils.go @@ -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) {