// 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)
}
Beispiel #2
0
// 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
}
Beispiel #3
0
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()
}
Beispiel #4
0
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())
}
Beispiel #5
0
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")
}
Beispiel #7
0
// 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)
}