func TestExportNonRootKeyReencrypt(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) require.NoError(t, err, "failed to create a temporary directory: %s", err) fileStore, err := trustmanager.NewKeyFileStore(tempBaseDir, oldPassphraseRetriever) cs := NewCryptoService(fileStore) pubKey, err := cs.Create(data.CanonicalSnapshotRole, gun, data.ECDSAKey) require.NoError(t, err) snapshotKeyID := pubKey.ID() tempKeyFile, err := ioutil.TempFile("", "notary-test-export-") tempKeyFilePath := tempKeyFile.Name() defer os.Remove(tempKeyFilePath) err = cs.ExportKeyReencrypt(tempKeyFile, snapshotKeyID, newPassphraseRetriever) require.NoError(t, err) tempKeyFile.Close() // Create new repo to test import tempBaseDir2, err := ioutil.TempDir("", "notary-test-") defer os.RemoveAll(tempBaseDir2) require.NoError(t, err, "failed to create a temporary directory: %s", err) fileStore2, err := trustmanager.NewKeyFileStore(tempBaseDir2, newPassphraseRetriever) cs2 := NewCryptoService(fileStore2) keyReader, err := os.Open(tempKeyFilePath) require.NoError(t, err, "could not open key file") pemBytes, err := ioutil.ReadAll(keyReader) require.NoError(t, err, "could not read key file") // Convert to a data.PrivateKey, potentially decrypting the key, and add it to the cryptoservice privKey, _, err := trustmanager.GetPasswdDecryptBytes(newPassphraseRetriever, pemBytes, "", "imported "+data.CanonicalSnapshotRole) require.NoError(t, err) err = cs2.AddKey(data.CanonicalSnapshotRole, gun, privKey) require.NoError(t, err) keyReader.Close() // Look for repo's snapshot key in repo2 // There should be a file named after the key ID of the snapshot key we // imported. snapshotKeyFilename := snapshotKeyID + ".key" _, err = os.Stat(filepath.Join(tempBaseDir2, notary.PrivDir, notary.NonRootKeysSubdir, "docker.com/notary", snapshotKeyFilename)) require.NoError(t, err, "missing snapshot key") // Should be able to unlock the root key with the new password key, alias, err := cs2.GetPrivateKey(snapshotKeyID) require.NoError(t, err, "could not unlock snapshot key") require.Equal(t, data.CanonicalSnapshotRole, alias) require.Equal(t, snapshotKeyID, key.ID()) }
func TestImportExportNonRootKey(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.CanonicalTargetsRole, data.ECDSAKey) assert.NoError(t, err) targetsKeyID := pubKey.ID() tempKeyFile, err := ioutil.TempFile("", "notary-test-export-") tempKeyFilePath := tempKeyFile.Name() defer os.Remove(tempKeyFilePath) err = cs.ExportKey(tempKeyFile, targetsKeyID, data.CanonicalTargetsRole) 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") pemBytes, err := ioutil.ReadAll(keyReader) assert.NoError(t, err, "could not read key file") err = cs2.ImportRoleKey(pemBytes, data.CanonicalTargetsRole, oldPassphraseRetriever) assert.NoError(t, err) keyReader.Close() // Look for repo's targets key in repo2 // There should be a file named after the key ID of the targets key we // imported. targetsKeyFilename := targetsKeyID + ".key" _, err = os.Stat(filepath.Join(tempBaseDir2, "private", "tuf_keys", "docker.com/notary", targetsKeyFilename)) assert.NoError(t, err, "missing targets key") // Check that the key is the same key, alias, err := cs2.GetPrivateKey(targetsKeyID) assert.NoError(t, err, "could not unlock targets key") assert.Equal(t, data.CanonicalTargetsRole, alias) assert.Equal(t, targetsKeyID, key.ID()) }
func TestImportExportRootKeyReencrypt(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.ExportRootKeyReencrypt(tempKeyFile, rootKeyID, newPassphraseRetriever) 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, newPassphraseRetriever) 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") // Should be able to unlock the root key with the new 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()) }
// ExportKeysByGUN exports all keys associated with a specified GUN to an // io.Writer in zip format. passphraseRetriever is used to select new passphrases to use to // encrypt the keys. func (cs *CryptoService) ExportKeysByGUN(dest io.Writer, gun string, passphraseRetriever passphrase.Retriever) error { tempBaseDir, err := ioutil.TempDir("", "notary-key-export-") defer os.RemoveAll(tempBaseDir) // Create temporary keystore to use as a staging area tempKeyStore, err := trustmanager.NewKeyFileStore(tempBaseDir, passphraseRetriever) if err != nil { return err } for _, ks := range cs.keyStores { if err := moveKeysByGUN(ks, tempKeyStore, gun); err != nil { return err } } zipWriter := zip.NewWriter(dest) if len(tempKeyStore.ListKeys()) == 0 { return ErrNoKeysFoundForGUN } if err := addKeysToArchive(zipWriter, tempKeyStore); err != nil { return err } zipWriter.Close() return nil }
// Export all the keys of a cryptoservice to a zipfile, and import it into a // new cryptoService, and return that new cryptoService func importExportedZip(t *testing.T, original *CryptoService, ret passphrase.Retriever, gun string) (*CryptoService, string) { // Temporary directory where test files will be created tempBaseDir, err := ioutil.TempDir("", "notary-test-") assert.NoError(t, err, "failed to create a temporary directory: %s", err) ks, err := trustmanager.NewKeyFileStore(tempBaseDir, ret) assert.NoError(t, err) var cs *CryptoService // export keys zipFile, err := ioutil.TempFile("", "notary-test-zipFile") defer os.RemoveAll(zipFile.Name()) if gun != "" { original.ExportKeysByGUN(zipFile, gun, ret) cs = NewCryptoService(gun, ks) } else { original.ExportAllKeys(zipFile, ret) cs = NewCryptoService(original.gun, ks) } zipFile.Close() // import keys into the cryptoservice now zipReader, err := zip.OpenReader(zipFile.Name()) assert.NoError(t, err) defer zipReader.Close() assert.NoError(t, cs.ImportKeysZip(zipReader.Reader)) return cs, tempBaseDir }
func getKeyStores(baseDir string, retriever notary.PassRetriever) ([]trustmanager.KeyStore, error) { fileKeyStore, err := trustmanager.NewKeyFileStore(baseDir, retriever) if err != nil { return nil, fmt.Errorf("failed to create private key store in directory: %s", baseDir) } return []trustmanager.KeyStore{fileKeyStore}, nil }
// Zips up the keys in the old repo, and assert that we can import it and use // said keys. The 0.1 exported format is just a zip file of all the keys func TestImport0Dot1Zip(t *testing.T) { ks, ret, _ := get0Dot1(t) zipFile, err := ioutil.TempFile("", "notary-test-zipFile") defer os.RemoveAll(zipFile.Name()) zipWriter := zip.NewWriter(zipFile) require.NoError(t, err) require.NoError(t, addKeysToArchive(zipWriter, ks)) zipWriter.Close() zipFile.Close() origKeys := make(map[string]string) for keyID, keyInfo := range ks.ListKeys() { origKeys[keyID] = keyInfo.Role } require.Len(t, origKeys, 3) // now import the zip file into a new cryptoservice tempDir, err := ioutil.TempDir("", "notary-test-import") defer os.RemoveAll(tempDir) require.NoError(t, err) ks, err = trustmanager.NewKeyFileStore(tempDir, ret) require.NoError(t, err) cs := NewCryptoService(ks) zipReader, err := zip.OpenReader(zipFile.Name()) require.NoError(t, err) defer zipReader.Close() require.NoError(t, cs.ImportKeysZip(zipReader.Reader, passphrase.ConstantRetriever("randompass"))) assertHasKeys(t, cs, origKeys) }
// NewNotaryRepository is a helper method that returns a new notary repository. // It takes the base directory under where all the trust files will be stored // (usually ~/.docker/trust/). func NewNotaryRepository(baseDir, gun, baseURL string) (*NotaryRepository, error) { trustDir := filepath.Join(baseDir, trustDir) rootKeysDir := filepath.Join(baseDir, rootKeysDir) privKeyStore, err := trustmanager.NewKeyFileStore(filepath.Join(baseDir, privDir)) if err != nil { return nil, err } signer := signed.NewSigner(NewCryptoService(gun, privKeyStore)) nRepo := &NotaryRepository{ Gun: gun, baseDir: baseDir, baseURL: baseURL, tufRepoPath: filepath.Join(baseDir, tufDir, gun), signer: signer, privKeyStore: privKeyStore, } if err := nRepo.loadKeys(trustDir, rootKeysDir); err != nil { return nil, err } return nRepo, nil }
func (k *keyCommander) getKeyStores( config *viper.Viper, withHardware, hardwareBackup bool) ([]trustmanager.KeyStore, error) { retriever := k.getRetriever() directory := config.GetString("trust_dir") fileKeyStore, err := trustmanager.NewKeyFileStore(directory, retriever) if err != nil { return nil, fmt.Errorf( "Failed to create private key store in directory: %s", directory) } ks := []trustmanager.KeyStore{fileKeyStore} if withHardware { var yubiStore trustmanager.KeyStore if hardwareBackup { yubiStore, err = getYubiStore(fileKeyStore, retriever) } else { yubiStore, err = getYubiStore(nil, retriever) } if err == nil && yubiStore != nil { // Note that the order is important, since we want to prioritize // the yubikey store ks = []trustmanager.KeyStore{yubiStore, fileKeyStore} } } return ks, nil }
// ExportKeysByGUN exports all keys associated with a specified GUN to an // io.Writer in zip format. passphraseRetriever is used to select new passphrases to use to // encrypt the keys. func (km *KeyStoreManager) ExportKeysByGUN(dest io.Writer, gun string, passphraseRetriever passphrase.Retriever) error { tempBaseDir, err := ioutil.TempDir("", "notary-key-export-") defer os.RemoveAll(tempBaseDir) privNonRootKeysSubdir := filepath.Join(privDir, nonRootKeysSubdir) // Create temporary keystore to use as a staging area tempNonRootKeysPath := filepath.Join(tempBaseDir, privNonRootKeysSubdir) tempNonRootKeyStore, err := trustmanager.NewKeyFileStore(tempNonRootKeysPath, passphraseRetriever) if err != nil { return err } if err := moveKeysByGUN(km.nonRootKeyStore, tempNonRootKeyStore, gun); err != nil { return err } zipWriter := zip.NewWriter(dest) if len(tempNonRootKeyStore.ListKeys()) == 0 { return ErrNoKeysFoundForGUN } if err := addKeysToArchive(zipWriter, tempNonRootKeyStore, privNonRootKeysSubdir); err != nil { return err } zipWriter.Close() return nil }
// ExportRootKeyReencrypt exports the specified root key to an io.Writer in // PEM format. The key is reencrypted with a new passphrase. func (cs *CryptoService) ExportRootKeyReencrypt(dest io.Writer, keyID string, newPassphraseRetriever passphrase.Retriever) error { privateKey, role, err := cs.GetPrivateKey(keyID) if err != nil { return err } // Create temporary keystore to use as a staging area tempBaseDir, err := ioutil.TempDir("", "notary-key-export-") defer os.RemoveAll(tempBaseDir) tempKeyStore, err := trustmanager.NewKeyFileStore(tempBaseDir, newPassphraseRetriever) if err != nil { return err } err = tempKeyStore.AddKey(keyID, role, privateKey) if err != nil { return err } pemBytes, err := tempKeyStore.ExportKey(keyID) if err != nil { return err } nBytes, err := dest.Write(pemBytes) if err != nil { return err } if nBytes != len(pemBytes) { return errors.New("Unable to finish writing exported key.") } return nil }
// ExportAllKeys exports all keys to an io.Writer in zip format. // newPassphraseRetriever will be used to obtain passphrases to use to encrypt the existing keys. func (cs *CryptoService) ExportAllKeys(dest io.Writer, newPassphraseRetriever notary.PassRetriever) error { tempBaseDir, err := ioutil.TempDir("", "notary-key-export-") defer os.RemoveAll(tempBaseDir) // Create temporary keystore to use as a staging area tempKeyStore, err := trustmanager.NewKeyFileStore(tempBaseDir, newPassphraseRetriever) if err != nil { return err } for _, ks := range cs.keyStores { if err := moveKeys(ks, tempKeyStore); err != nil { return err } } zipWriter := zip.NewWriter(dest) if err := addKeysToArchive(zipWriter, tempKeyStore); err != nil { return err } zipWriter.Close() return nil }
// ExportRootKeyReencrypt exports the specified root key to an io.Writer in // PEM format. The key is reencrypted with a new passphrase. func (km *KeyStoreManager) ExportRootKeyReencrypt(dest io.Writer, keyID string, newPassphraseRetriever passphrase.Retriever) error { privateKey, alias, err := km.rootKeyStore.GetKey(keyID) if err != nil { return err } // Create temporary keystore to use as a staging area tempBaseDir, err := ioutil.TempDir("", "notary-key-export-") defer os.RemoveAll(tempBaseDir) privRootKeysSubdir := filepath.Join(privDir, rootKeysSubdir) tempRootKeysPath := filepath.Join(tempBaseDir, privRootKeysSubdir) tempRootKeyStore, err := trustmanager.NewKeyFileStore(tempRootKeysPath, newPassphraseRetriever) if err != nil { return err } err = tempRootKeyStore.AddKey(keyID, alias, privateKey) if err != nil { return err } pemBytes, err := tempRootKeyStore.Get(keyID + "_" + alias) if err != nil { return err } _, err = dest.Write(pemBytes) return err }
// Zips up the keys in the old repo, and assert that we can import it and use // said keys. The 0.1 exported format is just a zip file of all the keys func TestImport0Dot1Zip(t *testing.T) { ks, ret, gun := get0Dot1(t) zipFile, err := ioutil.TempFile("", "notary-test-zipFile") defer os.RemoveAll(zipFile.Name()) zipWriter := zip.NewWriter(zipFile) assert.NoError(t, err) assert.NoError(t, addKeysToArchive(zipWriter, ks)) zipWriter.Close() zipFile.Close() origKeys := ks.ListKeys() assert.Len(t, origKeys, 3) // now import the zip file into a new cryptoservice tempDir, err := ioutil.TempDir("", "notary-test-import") defer os.RemoveAll(tempDir) assert.NoError(t, err) ks, err = trustmanager.NewKeyFileStore(tempDir, ret) assert.NoError(t, err) cs := NewCryptoService(gun, ks) zipReader, err := zip.OpenReader(zipFile.Name()) assert.NoError(t, err) defer zipReader.Close() assert.NoError(t, cs.ImportKeysZip(zipReader.Reader)) assertHasKeys(t, cs, origKeys) }
// Generates a Manager in a temporary directory and returns the // manager and certificates for two keys which have been added to the keystore. // Also returns the temporary directory so it can be cleaned up. func filestoreWithTwoCerts(t *testing.T, gun, keyAlg string) ( string, *Manager, *cryptoservice.CryptoService, []*x509.Certificate) { tempBaseDir, err := ioutil.TempDir("", "notary-test-") assert.NoError(t, err, "failed to create a temporary directory: %s", err) fileKeyStore, err := trustmanager.NewKeyFileStore(tempBaseDir, passphraseRetriever) assert.NoError(t, err) cryptoService := cryptoservice.NewCryptoService(gun, fileKeyStore) // Create a Manager certManager, err := NewManager(tempBaseDir) assert.NoError(t, err) certificates := make([]*x509.Certificate, 2) for i := 0; i < 2; i++ { pubKey, err := cryptoService.Create("root", keyAlg) assert.NoError(t, err) key, _, err := fileKeyStore.GetKey(pubKey.ID()) assert.NoError(t, err) cert, err := cryptoservice.GenerateTestingCertificate(key.CryptoSigner(), gun) assert.NoError(t, err) certificates[i] = cert } return tempBaseDir, certManager, cryptoService, certificates }
// ExportAllKeys exports all keys to an io.Writer in zip format. // newPassphraseRetriever will be used to obtain passphrases to use to encrypt the existing keys. func (km *KeyStoreManager) ExportAllKeys(dest io.Writer, newPassphraseRetriever passphrase.Retriever) error { tempBaseDir, err := ioutil.TempDir("", "notary-key-export-") defer os.RemoveAll(tempBaseDir) // Create temporary keystore to use as a staging area tempKeysPath := filepath.Join(tempBaseDir, privDir) tempKeyStore, err := trustmanager.NewKeyFileStore(tempKeysPath, newPassphraseRetriever) if err != nil { return err } if err := moveKeys(km.KeyStore, tempKeyStore); err != nil { return err } zipWriter := zip.NewWriter(dest) if err := addKeysToArchive(zipWriter, tempKeyStore, privDir); err != nil { return err } zipWriter.Close() return nil }
// NewKeyStoreManager returns an initialized KeyStoreManager, or an error // if it fails to create the KeyFileStores or load certificates func NewKeyStoreManager(baseDir string) (*KeyStoreManager, error) { nonRootKeysPath := filepath.Join(baseDir, privDir, nonRootKeysSubdir) nonRootKeyStore, err := trustmanager.NewKeyFileStore(nonRootKeysPath) if err != nil { return nil, err } // Load the keystore that will hold all of our encrypted Root Private Keys rootKeysPath := filepath.Join(baseDir, privDir, rootKeysSubdir) rootKeyStore, err := trustmanager.NewKeyFileStore(rootKeysPath) if err != nil { return nil, err } trustPath := filepath.Join(baseDir, trustDir) // Load all CAs that aren't expired and don't use SHA1 trustedCAStore, err := trustmanager.NewX509FilteredFileStore(trustPath, func(cert *x509.Certificate) bool { return cert.IsCA && cert.BasicConstraintsValid && cert.SubjectKeyId != nil && time.Now().Before(cert.NotAfter) && cert.SignatureAlgorithm != x509.SHA1WithRSA && cert.SignatureAlgorithm != x509.DSAWithSHA1 && cert.SignatureAlgorithm != x509.ECDSAWithSHA1 }) if err != nil { return nil, err } // Load all individual (non-CA) certificates that aren't expired and don't use SHA1 trustedCertificateStore, err := trustmanager.NewX509FilteredFileStore(trustPath, func(cert *x509.Certificate) bool { return !cert.IsCA && time.Now().Before(cert.NotAfter) && cert.SignatureAlgorithm != x509.SHA1WithRSA && cert.SignatureAlgorithm != x509.DSAWithSHA1 && cert.SignatureAlgorithm != x509.ECDSAWithSHA1 }) if err != nil { return nil, err } return &KeyStoreManager{ rootKeyStore: rootKeyStore, nonRootKeyStore: nonRootKeyStore, trustedCAStore: trustedCAStore, trustedCertificateStore: trustedCertificateStore, }, nil }
func get0Dot1(t *testing.T) (*trustmanager.KeyFileStore, passphrase.Retriever, string) { gun := "docker.io/notary0.1/samplerepo" ret := passphrase.ConstantRetriever("randompass") // produce the zip file ks, err := trustmanager.NewKeyFileStore("../fixtures/compatibility/notary0.1", ret) assert.NoError(t, err) return ks, ret, gun }
// NewNotaryRepository is a helper method that returns a new notary repository. // It takes the base directory under where all the trust files will be stored // (usually ~/.docker/trust/). func NewNotaryRepository(baseDir, gun, baseURL string, rt http.RoundTripper, retriever passphrase.Retriever, trustPinning trustpinning.TrustPinConfig) ( *NotaryRepository, error) { fileKeyStore, err := trustmanager.NewKeyFileStore(baseDir, retriever) if err != nil { return nil, fmt.Errorf("failed to create private key store in directory: %s", baseDir) } return repositoryFromKeystores(baseDir, gun, baseURL, rt, []trustmanager.KeyStore{fileKeyStore}, trustPinning) }
// reads data from the repository in order to fake data being served via // the ServeMux. func fakeServerData(t *testing.T, repo *NotaryRepository, mux *http.ServeMux) { tempKey, err := data.UnmarshalPrivateKey([]byte(timestampECDSAKeyJSON)) assert.NoError(t, err) savedTUFRepo := repo.tufRepo // in case this is overwritten fileStore, err := trustmanager.NewKeyFileStore(repo.baseDir, passphraseRetriever) assert.NoError(t, err) fileStore.AddKey( filepath.Join(filepath.FromSlash(repo.gun), tempKey.ID()), "nonroot", tempKey) rootJSONFile := filepath.Join(repo.baseDir, "tuf", filepath.FromSlash(repo.gun), "metadata", "root.json") rootFileBytes, err := ioutil.ReadFile(rootJSONFile) signedTargets, err := savedTUFRepo.SignTargets( "targets", data.DefaultExpires("targets")) assert.NoError(t, err) signedSnapshot, err := savedTUFRepo.SignSnapshot( data.DefaultExpires("snapshot")) assert.NoError(t, err) signedTimestamp, err := savedTUFRepo.SignTimestamp( data.DefaultExpires("timestamp")) assert.NoError(t, err) mux.HandleFunc("/v2/docker.com/notary/_trust/tuf/root.json", func(w http.ResponseWriter, r *http.Request) { assert.NoError(t, err) fmt.Fprint(w, string(rootFileBytes)) }) mux.HandleFunc("/v2/docker.com/notary/_trust/tuf/timestamp.json", func(w http.ResponseWriter, r *http.Request) { timestampJSON, _ := json.Marshal(signedTimestamp) fmt.Fprint(w, string(timestampJSON)) }) mux.HandleFunc("/v2/docker.com/notary/_trust/tuf/snapshot.json", func(w http.ResponseWriter, r *http.Request) { snapshotJSON, _ := json.Marshal(signedSnapshot) fmt.Fprint(w, string(snapshotJSON)) }) mux.HandleFunc("/v2/docker.com/notary/_trust/tuf/targets.json", func(w http.ResponseWriter, r *http.Request) { targetsJSON, _ := json.Marshal(signedTargets) fmt.Fprint(w, string(targetsJSON)) }) }
func getKeyStores(baseDir string, retriever notary.PassRetriever) ([]trustmanager.KeyStore, error) { fileKeyStore, err := trustmanager.NewKeyFileStore(baseDir, retriever) if err != nil { return nil, fmt.Errorf("failed to create private key store in directory: %s", baseDir) } keyStores := []trustmanager.KeyStore{fileKeyStore} yubiKeyStore, _ := yubikey.NewYubiStore(fileKeyStore, retriever) if yubiKeyStore != nil { keyStores = []trustmanager.KeyStore{yubiKeyStore, fileKeyStore} } return keyStores, nil }
// keyPassphraseChange changes the passphrase for a root key's private key based on ID func (k *keyCommander) keyPassphraseChange(cmd *cobra.Command, args []string) error { if len(args) < 1 { cmd.Usage() return fmt.Errorf("must specify the key ID of the key to change the passphrase of") } config, err := k.configGetter() if err != nil { return err } ks, err := k.getKeyStores(config, true) if err != nil { return err } keyID := args[0] // This is an invalid ID if len(keyID) != notary.Sha256HexSize { return fmt.Errorf("invalid key ID provided: %s", keyID) } // Find the key's GUN by ID, in case it is a non-root key var keyGUN string for _, store := range ks { for keypath := range store.ListKeys() { if filepath.Base(keypath) == keyID { keyGUN = filepath.Dir(keypath) } } } cs := cryptoservice.NewCryptoService(keyGUN, ks...) privKey, role, err := cs.GetPrivateKey(keyID) if err != nil { return fmt.Errorf("could not retrieve local key for key ID provided: %s", keyID) } // Must use a different passphrase retriever to avoid caching the // unlocking passphrase and reusing that. passChangeRetriever := k.getRetriever() keyStore, err := trustmanager.NewKeyFileStore(config.GetString("trust_dir"), passChangeRetriever) err = keyStore.AddKey(filepath.Join(keyGUN, keyID), role, privKey) if err != nil { return err } cmd.Println("") cmd.Printf("Successfully updated passphrase for key ID: %s", keyID) cmd.Println("") return nil }
// ExportAllKeys exports all keys to an io.Writer in zip format. // outputPassphrase is the new passphrase to use to encrypt the existing keys. // If blank, the keys will not be encrypted. Note that keys which are already // encrypted are not re-encrypted. They will be included in the zip with their // original encryption. func (km *KeyStoreManager) ExportAllKeys(dest io.Writer, outputPassphrase string) error { tempBaseDir, err := ioutil.TempDir("", "notary-key-export-") defer os.RemoveAll(tempBaseDir) // Create temporary keystores to use as a staging area tempNonRootKeysPath := filepath.Join(tempBaseDir, privDir, nonRootKeysSubdir) tempNonRootKeyStore, err := trustmanager.NewKeyFileStore(tempNonRootKeysPath) if err != nil { return err } tempRootKeysPath := filepath.Join(tempBaseDir, privDir, rootKeysSubdir) tempRootKeyStore, err := trustmanager.NewKeyFileStore(tempRootKeysPath) if err != nil { return err } if err := moveKeysWithNewPassphrase(km.rootKeyStore, tempRootKeyStore, outputPassphrase); err != nil { return err } if err := moveKeysWithNewPassphrase(km.nonRootKeyStore, tempNonRootKeyStore, outputPassphrase); err != nil { return err } zipWriter := zip.NewWriter(dest) if err := addKeysToArchive(zipWriter, tempRootKeyStore, tempBaseDir); err != nil { return err } if err := addKeysToArchive(zipWriter, tempNonRootKeyStore, tempBaseDir); err != nil { return err } zipWriter.Close() return nil }
// NewNotaryRepository is a helper method that returns a new notary repository. // It takes the base directory under where all the trust files will be stored // (usually ~/.docker/trust/). func NewNotaryRepository(baseDir, gun, baseURL string, rt http.RoundTripper, retriever passphrase.Retriever) ( *NotaryRepository, error) { fileKeyStore, err := trustmanager.NewKeyFileStore(baseDir, retriever) if err != nil { return nil, fmt.Errorf("failed to create private key store in directory: %s", baseDir) } keyStores := []trustmanager.KeyStore{fileKeyStore} yubiKeyStore, _ := yubikey.NewYubiKeyStore(fileKeyStore, retriever) if yubiKeyStore != nil { keyStores = append(keyStores, yubiKeyStore) } return repositoryFromKeystores(baseDir, gun, baseURL, rt, keyStores) }
// NewNotaryRepository is a helper method that returns a new notary repository. // It takes the base directory under where all the trust files will be stored // (usually ~/.docker/trust/). func NewNotaryRepository(baseDir, gun, baseURL string, rt http.RoundTripper, retriever notary.PassRetriever, trustPinning trustpinning.TrustPinConfig) ( *NotaryRepository, error) { fileKeyStore, err := trustmanager.NewKeyFileStore(baseDir, retriever) if err != nil { return nil, fmt.Errorf("failed to create private key store in directory: %s", baseDir) } keyStores := []trustmanager.KeyStore{fileKeyStore} yubiKeyStore, _ := yubikey.NewYubiStore(fileKeyStore, retriever) if yubiKeyStore != nil { keyStores = []trustmanager.KeyStore{yubiKeyStore, fileKeyStore} } return repositoryFromKeystores(baseDir, gun, baseURL, rt, keyStores, trustPinning) }
func getKeyStores(cmd *cobra.Command, directory string, ret passphrase.Retriever, withHardware bool) []trustmanager.KeyStore { fileKeyStore, err := trustmanager.NewKeyFileStore(directory, ret) if err != nil { fatalf("Failed to create private key store in directory: %s", directory) } ks := []trustmanager.KeyStore{fileKeyStore} if withHardware { yubiStore, err := getYubiKeyStore(fileKeyStore, ret) if err == nil && yubiStore != nil { // Note that the order is important, since we want to prioritize // the yubikey store ks = []trustmanager.KeyStore{yubiStore, fileKeyStore} } } return ks }
// NewNotaryRepository is a helper method that returns a new notary repository. // It takes the base directory under where all the trust files will be stored // (usually ~/.docker/trust/). func NewNotaryRepository(baseDir, gun, baseURL string, rt http.RoundTripper, retriever passphrase.Retriever) (*NotaryRepository, error) { fileKeyStore, err := trustmanager.NewKeyFileStore(baseDir, retriever) if err != nil { return nil, fmt.Errorf("failed to create private key store in directory: %s", baseDir) } keyStoreManager, err := keystoremanager.NewKeyStoreManager(baseDir) yubiKeyStore, _ := yubikey.NewYubiKeyStore(fileKeyStore, retriever) var cryptoService signed.CryptoService if yubiKeyStore == nil { cryptoService = cryptoservice.NewCryptoService(gun, fileKeyStore) } else { cryptoService = cryptoservice.NewCryptoService(gun, yubiKeyStore, fileKeyStore) } nRepo := &NotaryRepository{ gun: gun, baseDir: baseDir, baseURL: baseURL, tufRepoPath: filepath.Join(baseDir, tufDir, filepath.FromSlash(gun)), CryptoService: cryptoService, roundTrip: rt, KeyStoreManager: keyStoreManager, } fileStore, err := store.NewFilesystemStore( nRepo.tufRepoPath, "metadata", "json", "", ) if err != nil { return nil, err } nRepo.fileStore = fileStore return nRepo, nil }
func TestImportExport0Dot1GUNKeys(t *testing.T) { ks, ret, gun := get0Dot1(t) // remove root from expected key list, because root is not exported when // we export by gun expectedKeys := make(map[string]string) for keyID, role := range ks.ListKeys() { if role != data.CanonicalRootRole { expectedKeys[keyID] = role } } // make some other temp directory to create new keys in tempDir, err := ioutil.TempDir("", "notary-tests-keystore") defer os.RemoveAll(tempDir) assert.NoError(t, err) otherKS, err := trustmanager.NewKeyFileStore(tempDir, ret) assert.NoError(t, err) cs := NewCryptoService("some/other/gun", otherKS, ks) // create a keys that is not of the same GUN, and be sure it's in this // CryptoService otherPubKey, err := cs.Create(data.CanonicalTargetsRole, data.ECDSAKey) assert.NoError(t, err) k, _, err := cs.GetPrivateKey(otherPubKey.ID()) assert.NoError(t, err) assert.NotNil(t, k) // export/import, and ensure that the other-gun key is not in the new // CryptoService newCS, tempDir := importExportedZip(t, cs, ret, gun) defer os.RemoveAll(tempDir) assertHasKeys(t, newCS, expectedKeys) _, _, err = newCS.GetPrivateKey(otherPubKey.ID()) assert.Error(t, err) }
// NewKeyStoreManager returns an initialized KeyStoreManager, or an error // if it fails to create the KeyFileStores or load certificates func NewKeyStoreManager(baseDir string, passphraseRetriever passphrase.Retriever) (*KeyStoreManager, error) { keysPath := filepath.Join(baseDir, privDir) keyStore, err := trustmanager.NewKeyFileStore(keysPath, passphraseRetriever) if err != nil { return nil, err } trustPath := filepath.Join(baseDir, trustDir) // Load all CAs that aren't expired and don't use SHA1 trustedCAStore, err := trustmanager.NewX509FilteredFileStore(trustPath, func(cert *x509.Certificate) bool { return cert.IsCA && cert.BasicConstraintsValid && cert.SubjectKeyId != nil && time.Now().Before(cert.NotAfter) && cert.SignatureAlgorithm != x509.SHA1WithRSA && cert.SignatureAlgorithm != x509.DSAWithSHA1 && cert.SignatureAlgorithm != x509.ECDSAWithSHA1 }) if err != nil { return nil, err } // Load all individual (non-CA) certificates that aren't expired and don't use SHA1 trustedCertificateStore, err := trustmanager.NewX509FilteredFileStore(trustPath, func(cert *x509.Certificate) bool { return !cert.IsCA && time.Now().Before(cert.NotAfter) && cert.SignatureAlgorithm != x509.SHA1WithRSA && cert.SignatureAlgorithm != x509.DSAWithSHA1 && cert.SignatureAlgorithm != x509.ECDSAWithSHA1 }) if err != nil { return nil, err } return &KeyStoreManager{ KeyStore: keyStore, trustedCAStore: trustedCAStore, trustedCertificateStore: trustedCertificateStore, }, nil }
func (r *NotaryRepository) loadKeys(trustDir, rootKeysDir string) error { // Load all CAs that aren't expired and don't use SHA1 caStore, err := trustmanager.NewX509FilteredFileStore(trustDir, func(cert *x509.Certificate) bool { return cert.IsCA && cert.BasicConstraintsValid && cert.SubjectKeyId != nil && time.Now().Before(cert.NotAfter) && cert.SignatureAlgorithm != x509.SHA1WithRSA && cert.SignatureAlgorithm != x509.DSAWithSHA1 && cert.SignatureAlgorithm != x509.ECDSAWithSHA1 }) if err != nil { return err } // Load all individual (non-CA) certificates that aren't expired and don't use SHA1 certificateStore, err := trustmanager.NewX509FilteredFileStore(trustDir, func(cert *x509.Certificate) bool { return !cert.IsCA && time.Now().Before(cert.NotAfter) && cert.SignatureAlgorithm != x509.SHA1WithRSA && cert.SignatureAlgorithm != x509.DSAWithSHA1 && cert.SignatureAlgorithm != x509.ECDSAWithSHA1 }) if err != nil { 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 } r.caStore = caStore r.certificateStore = certificateStore r.rootKeyStore = rootKeyStore return nil }