// generateKeyAndCert deals with the creation and storage of a key and returns a cert func generateKeyAndCert(gun string) (crypto.PrivateKey, *x509.Certificate, error) { // Generates a new RSA key key, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { return nil, nil, fmt.Errorf("could not generate private key: %v", err) } // Creates a new Certificate template. We need the certificate to calculate the // TUF-compliant keyID //TODO (diogo): We're hardcoding the Organization to be the GUN. Probably want to // change it template := newCertificate(gun, gun) derBytes, err := x509.CreateCertificate(rand.Reader, template, template, key.Public(), key) if err != nil { return nil, nil, fmt.Errorf("failed to generate the certificate for key: %v", err) } // Encode the new certificate into PEM cert, err := x509.ParseCertificate(derBytes) if err != nil { return nil, nil, fmt.Errorf("failed to generate the certificate for key: %v", err) } fingerprint := trustmanager.FingerprintCert(cert) // The key is going to be stored in the private directory, using the GUN and // the filename will be the TUF-compliant ID. The Store takes care of extensions. privKeyFilename := filepath.Join(gun, fingerprint) pemKey, err := trustmanager.KeyToPEM(key) if err != nil { return nil, nil, fmt.Errorf("failed to generate the certificate for key: %v", err) } return key, cert, privKeyStore.Add(privKeyFilename, pemKey) }
// ImportRoleKey imports a private key in PEM format key from a byte array // It prompts for the key's passphrase to verify the data and to determine // the key ID. func (cs *CryptoService) ImportRoleKey(pemBytes []byte, role string, newPassphraseRetriever passphrase.Retriever) error { var alias string var err error if role == data.CanonicalRootRole { alias = role if err = checkRootKeyIsEncrypted(pemBytes); err != nil { return err } } else { // Parse the private key to get the key ID so that we can import it to the correct location privKey, err := trustmanager.ParsePEMPrivateKey(pemBytes, "") if err != nil { privKey, _, err = trustmanager.GetPasswdDecryptBytes(newPassphraseRetriever, pemBytes, role, string(role)) if err != nil { return err } } // Since we're importing a non-root role, we need to pass the path as an alias alias = filepath.Join(notary.NonRootKeysSubdir, cs.gun, privKey.ID()) // We also need to ensure that the role is properly set in the PEM headers pemBytes, err = trustmanager.KeyToPEM(privKey, role) if err != nil { return err } } for _, ks := range cs.keyStores { // don't redeclare err, we want the value carried out of the loop if err = ks.ImportKey(pemBytes, alias); err == nil { return nil //bail on the first keystore we import to } } return err }
func generateTempTestKeyFile(t *testing.T, role string) string { privKey, err := trustmanager.GenerateECDSAKey(rand.Reader) if err != nil { return "" } keyBytes, err := trustmanager.KeyToPEM(privKey, role) assert.NoError(t, err) tempPrivFile, err := ioutil.TempFile("/tmp", "privfile") assert.NoError(t, err) // Write the private key to a file so we can import it _, err = tempPrivFile.Write(keyBytes) assert.NoError(t, err) tempPrivFile.Close() return tempPrivFile.Name() }
func TestImportExportRootKey(t *testing.T) { gun := "docker.com/notary" // Temporary directory where test files will be created tempBaseDir, err := ioutil.TempDir("", "notary-test-") defer os.RemoveAll(tempBaseDir) assert.NoError(t, err, "failed to create a temporary directory: %s", err) ts, _ := createTestServer(t) defer ts.Close() repo, err := client.NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, oldPassphraseRetriever) assert.NoError(t, err, "error creating repo: %s", err) rootKeyID, err := repo.KeyStoreManager.GenRootKey(data.ECDSAKey.String()) assert.NoError(t, err, "error generating root key: %s", err) rootCryptoService, err := repo.KeyStoreManager.GetRootCryptoService(rootKeyID) assert.NoError(t, err, "error retrieving root key: %s", err) err = repo.Initialize(rootCryptoService) assert.NoError(t, err, "error creating repository: %s", err) tempKeyFile, err := ioutil.TempFile("", "notary-test-export-") tempKeyFilePath := tempKeyFile.Name() defer os.Remove(tempKeyFilePath) err = repo.KeyStoreManager.ExportRootKey(tempKeyFile, rootKeyID) assert.NoError(t, err) tempKeyFile.Close() // Create new repo to test import tempBaseDir2, err := ioutil.TempDir("", "notary-test-") defer os.RemoveAll(tempBaseDir2) assert.NoError(t, err, "failed to create a temporary directory: %s", err) repo2, err := client.NewNotaryRepository(tempBaseDir2, gun, ts.URL, http.DefaultTransport, oldPassphraseRetriever) assert.NoError(t, err, "error creating repo: %s", err) rootKeyID2, err := repo2.KeyStoreManager.GenRootKey(data.ECDSAKey.String()) assert.NoError(t, err, "error generating root key: %s", err) rootCryptoService2, err := repo2.KeyStoreManager.GetRootCryptoService(rootKeyID2) assert.NoError(t, err, "error retrieving root key: %s", err) err = repo2.Initialize(rootCryptoService2) assert.NoError(t, err, "error creating repository: %s", err) keyReader, err := os.Open(tempKeyFilePath) assert.NoError(t, err, "could not open key file") err = repo2.KeyStoreManager.ImportRootKey(keyReader, rootKeyID) assert.NoError(t, err) keyReader.Close() // Look for repo's root key in repo2 // There should be a file named after the key ID of the root key we // imported. rootKeyFilename := rootKeyID + "_root.key" _, err = os.Stat(filepath.Join(tempBaseDir2, "private", "root_keys", rootKeyFilename)) assert.NoError(t, err, "missing root key") // Try to import a decrypted version of the root key and make sure it // doesn't succeed pemBytes, err := ioutil.ReadFile(tempKeyFilePath) assert.NoError(t, err, "could not read key file") privKey, err := trustmanager.ParsePEMPrivateKey(pemBytes, oldPassphrase) assert.NoError(t, err, "could not decrypt key file") decryptedPEMBytes, err := trustmanager.KeyToPEM(privKey) assert.NoError(t, err, "could not convert key to PEM") err = repo2.KeyStoreManager.ImportRootKey(bytes.NewReader(decryptedPEMBytes), rootKeyID) assert.EqualError(t, err, keystoremanager.ErrRootKeyNotEncrypted.Error()) // Try to import garbage and make sure it doesn't succeed err = repo2.KeyStoreManager.ImportRootKey(strings.NewReader("this is not PEM"), rootKeyID) assert.EqualError(t, err, keystoremanager.ErrNoValidPrivateKey.Error()) // Should be able to unlock the root key with the old password key, alias, err := repo2.KeyStoreManager.RootKeyStore().GetKey(rootKeyID) assert.NoError(t, err, "could not unlock root key") assert.Equal(t, "root", alias) assert.Equal(t, rootKeyID, key.ID()) }
func TestImportExportRootKey(t *testing.T) { gun := "docker.com/notary" // Temporary directory where test files will be created tempBaseDir, err := ioutil.TempDir("", "notary-test-") defer os.RemoveAll(tempBaseDir) assert.NoError(t, err, "failed to create a temporary directory: %s", err) fileStore, err := trustmanager.NewKeyFileStore(tempBaseDir, oldPassphraseRetriever) cs := NewCryptoService(gun, fileStore) pubKey, err := cs.Create(data.CanonicalRootRole, data.ECDSAKey) assert.NoError(t, err) rootKeyID := pubKey.ID() tempKeyFile, err := ioutil.TempFile("", "notary-test-export-") tempKeyFilePath := tempKeyFile.Name() defer os.Remove(tempKeyFilePath) err = cs.ExportRootKey(tempKeyFile, rootKeyID) assert.NoError(t, err) tempKeyFile.Close() // Create new repo to test import tempBaseDir2, err := ioutil.TempDir("", "notary-test-") defer os.RemoveAll(tempBaseDir2) assert.NoError(t, err, "failed to create a temporary directory: %s", err) fileStore2, err := trustmanager.NewKeyFileStore(tempBaseDir2, oldPassphraseRetriever) cs2 := NewCryptoService(gun, fileStore2) keyReader, err := os.Open(tempKeyFilePath) assert.NoError(t, err, "could not open key file") err = cs2.ImportRootKey(keyReader) assert.NoError(t, err) keyReader.Close() // Look for repo's root key in repo2 // There should be a file named after the key ID of the root key we // imported. rootKeyFilename := rootKeyID + ".key" _, err = os.Stat(filepath.Join(tempBaseDir2, "private", "root_keys", rootKeyFilename)) assert.NoError(t, err, "missing root key") // Try to import a decrypted version of the root key and make sure it // doesn't succeed pemBytes, err := ioutil.ReadFile(tempKeyFilePath) assert.NoError(t, err, "could not read key file") privKey, err := trustmanager.ParsePEMPrivateKey(pemBytes, oldPassphrase) assert.NoError(t, err, "could not decrypt key file") decryptedPEMBytes, err := trustmanager.KeyToPEM(privKey, "root") assert.NoError(t, err, "could not convert key to PEM") err = cs2.ImportRootKey(bytes.NewReader(decryptedPEMBytes)) assert.EqualError(t, err, ErrRootKeyNotEncrypted.Error()) // Try to import garbage and make sure it doesn't succeed err = cs2.ImportRootKey(strings.NewReader("this is not PEM")) assert.EqualError(t, err, ErrNoValidPrivateKey.Error()) // Should be able to unlock the root key with the old password key, alias, err := cs2.GetPrivateKey(rootKeyID) assert.NoError(t, err, "could not unlock root key") assert.Equal(t, "root", alias) assert.Equal(t, rootKeyID, key.ID()) }
// Initialize repo and test publishing targets with delegation roles func TestClientDelegationsPublishing(t *testing.T) { setUp(t) tempDir := tempDirWithConfig(t, "{}") defer os.RemoveAll(tempDir) server := setupServer() defer server.Close() // Setup certificate for delegation role tempFile, err := ioutil.TempFile("", "pemfile") assert.NoError(t, err) privKey, err := trustmanager.GenerateRSAKey(rand.Reader, 2048) assert.NoError(t, err) privKeyBytesNoRole, err := trustmanager.KeyToPEM(privKey, "") assert.NoError(t, err) privKeyBytesWithRole, err := trustmanager.KeyToPEM(privKey, "user") assert.NoError(t, err) startTime := time.Now() endTime := startTime.AddDate(10, 0, 0) cert, err := cryptoservice.GenerateCertificate(privKey, "gun", startTime, endTime) assert.NoError(t, err) _, err = tempFile.Write(trustmanager.CertToPEM(cert)) assert.NoError(t, err) tempFile.Close() defer os.Remove(tempFile.Name()) rawPubBytes, _ := ioutil.ReadFile(tempFile.Name()) parsedPubKey, _ := trustmanager.ParsePEMPublicKey(rawPubBytes) canonicalKeyID, err := utils.CanonicalKeyID(parsedPubKey) assert.NoError(t, err) // Set up targets for publishing tempTargetFile, err := ioutil.TempFile("", "targetfile") assert.NoError(t, err) tempTargetFile.Close() defer os.Remove(tempTargetFile.Name()) var target = "sdgkadga" var output string // init repo _, err = runCommand(t, tempDir, "-s", server.URL, "init", "gun") assert.NoError(t, err) // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list delegations - none yet output, err = runCommand(t, tempDir, "-s", server.URL, "delegation", "list", "gun") assert.NoError(t, err) assert.Contains(t, output, "No delegations present in this repository.") // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // validate that we have all keys, including snapshot assertNumKeys(t, tempDir, 1, 2, true) // rotate the snapshot key to server output, err = runCommand(t, tempDir, "-s", server.URL, "key", "rotate", "gun", "-r", "--key-type", "snapshot") assert.NoError(t, err) // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // validate that we lost the snapshot signing key _, signingKeyIDs := assertNumKeys(t, tempDir, 1, 1, true) targetKeyID := signingKeyIDs[0] // add new valid delegation with single new cert output, err = runCommand(t, tempDir, "delegation", "add", "gun", "targets/releases", tempFile.Name(), "--paths", "\"\"") assert.NoError(t, err) assert.Contains(t, output, "Addition of delegation role") // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list delegations - we should see our one delegation output, err = runCommand(t, tempDir, "-s", server.URL, "delegation", "list", "gun") assert.NoError(t, err) assert.NotContains(t, output, "No delegations present in this repository.") // remove the targets key to demonstrate that delegates don't need this key keyDir := filepath.Join(tempDir, "private", "tuf_keys") assert.NoError(t, os.Remove(filepath.Join(keyDir, "gun", targetKeyID+".key"))) // Note that we need to use the canonical key ID, followed by the base of the role here err = ioutil.WriteFile(filepath.Join(keyDir, canonicalKeyID+"_releases.key"), privKeyBytesNoRole, 0700) assert.NoError(t, err) // add a target using the delegation -- will only add to targets/releases _, err = runCommand(t, tempDir, "add", "gun", target, tempTargetFile.Name(), "--roles", "targets/releases") assert.NoError(t, err) // list targets for targets/releases - we should see no targets until we publish output, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun", "--roles", "targets/releases") assert.NoError(t, err) assert.Contains(t, output, "No targets") output, err = runCommand(t, tempDir, "-s", server.URL, "status", "gun") assert.NoError(t, err) // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list targets for targets/releases - we should see our target! output, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun", "--roles", "targets/releases") assert.NoError(t, err) assert.Contains(t, output, "targets/releases") // remove the target for this role only _, err = runCommand(t, tempDir, "remove", "gun", target, "--roles", "targets/releases") assert.NoError(t, err) // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list targets for targets/releases - we should see no targets output, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun", "--roles", "targets/releases") assert.NoError(t, err) assert.Contains(t, output, "No targets present") // Try adding a target with a different key style - private/tuf_keys/canonicalKeyID.key with "user" set as the "role" PEM header // First remove the old key and add the new style assert.NoError(t, os.Remove(filepath.Join(keyDir, canonicalKeyID+"_releases.key"))) err = ioutil.WriteFile(filepath.Join(keyDir, canonicalKeyID+".key"), privKeyBytesWithRole, 0700) assert.NoError(t, err) // add a target using the delegation -- will only add to targets/releases _, err = runCommand(t, tempDir, "add", "gun", target, tempTargetFile.Name(), "--roles", "targets/releases") assert.NoError(t, err) // list targets for targets/releases - we should see no targets until we publish output, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun", "--roles", "targets/releases") assert.NoError(t, err) assert.Contains(t, output, "No targets") // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list targets for targets/releases - we should see our target! output, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun", "--roles", "targets/releases") assert.NoError(t, err) assert.Contains(t, output, "targets/releases") // remove the target for this role only _, err = runCommand(t, tempDir, "remove", "gun", target, "--roles", "targets/releases") assert.NoError(t, err) // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // Now remove this key, and make a new file to import the delegation's key from assert.NoError(t, os.Remove(filepath.Join(keyDir, canonicalKeyID+".key"))) tempPrivFile, err := ioutil.TempFile("/tmp", "privfile") assert.NoError(t, err) defer os.Remove(tempPrivFile.Name()) // Write the private key to a file so we can import it _, err = tempPrivFile.Write(privKeyBytesNoRole) assert.NoError(t, err) tempPrivFile.Close() // Import the private key, associating it with our delegation role _, err = runCommand(t, tempDir, "key", "import", tempPrivFile.Name(), "--role", "targets/releases") assert.NoError(t, err) // add a target using the delegation -- will only add to targets/releases _, err = runCommand(t, tempDir, "add", "gun", target, tempTargetFile.Name(), "--roles", "targets/releases") assert.NoError(t, err) // list targets for targets/releases - we should see no targets until we publish output, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun", "--roles", "targets/releases") assert.NoError(t, err) assert.Contains(t, output, "No targets") // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list targets for targets/releases - we should see our target! output, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun", "--roles", "targets/releases") assert.NoError(t, err) assert.Contains(t, output, "targets/releases") }
// Tests import/export root key only func TestClientKeyImportExportRootOnly(t *testing.T) { // -- setup -- setUp(t) tempDir := tempDirWithConfig(t, "{}") defer os.RemoveAll(tempDir) server := setupServer() defer server.Close() var ( target = "sdgkadga" rootKeyID string ) tempFile, err := ioutil.TempFile("", "pemfile") require.NoError(t, err) // close later, because we might need to write to it defer os.Remove(tempFile.Name()) // -- tests -- if rootOnHardware() { t.Log("Cannot export a key from hardware. Will generate one to import.") privKey, err := trustmanager.GenerateECDSAKey(rand.Reader) require.NoError(t, err) pemBytes, err := trustmanager.EncryptPrivateKey(privKey, "root", testPassphrase) require.NoError(t, err) nBytes, err := tempFile.Write(pemBytes) require.NoError(t, err) tempFile.Close() require.Equal(t, len(pemBytes), nBytes) rootKeyID = privKey.ID() } else { tempFile.Close() rootKeyID = exportRoot(t, tempFile.Name()) } // import the key _, err = runCommand(t, tempDir, "key", "import", tempFile.Name()) require.NoError(t, err) // if there is hardware available, root will only be on hardware, and not // on disk newRoot, _ := assertNumKeys(t, tempDir, 1, 0, !rootOnHardware()) require.Equal(t, rootKeyID, newRoot[0]) // Just to make sure, init a repo and publish _, err = runCommand(t, tempDir, "-s", server.URL, "init", "gun") require.NoError(t, err) assertNumKeys(t, tempDir, 1, 2, !rootOnHardware()) assertSuccessfullyPublish( t, tempDir, server.URL, "gun", target, tempFile.Name()) // Now assert that bad root keys give an error // Try importing an unencrypted root key: privKey, err := trustmanager.GenerateECDSAKey(rand.Reader) require.NoError(t, err) decryptedPEMBytes, err := trustmanager.KeyToPEM(privKey, data.CanonicalRootRole) decryptedKeyFile, err := ioutil.TempFile("", "decryptedPem") require.NoError(t, err) // close later, because we might need to write to it defer os.Remove(decryptedKeyFile.Name()) nBytes, err := decryptedKeyFile.Write(decryptedPEMBytes) require.NoError(t, err) decryptedKeyFile.Close() require.Equal(t, len(decryptedPEMBytes), nBytes) // import the key _, err = runCommand(t, tempDir, "key", "import", decryptedKeyFile.Name()) require.Error(t, err) // Now try importing an invalid PEM as a root key invalidPEMBytes := []byte("this is not PEM") invalidPEMFile, err := ioutil.TempFile("", "invalidPem") require.NoError(t, err) // close later, because we might need to write to it defer os.Remove(invalidPEMFile.Name()) nBytes, err = invalidPEMFile.Write(invalidPEMBytes) require.NoError(t, err) invalidPEMFile.Close() require.Equal(t, len(invalidPEMBytes), nBytes) // import the key _, err = runCommand(t, tempDir, "key", "import", invalidPEMFile.Name()) require.Error(t, err) }