keystore aliasing, take 1

Signed-off-by: Nathan McCauley <nathan.mccauley@docker.com>
This commit is contained in:
Nathan McCauley 2015-07-18 02:01:55 -07:00
parent 7530774101
commit 5df1eb21f3
7 changed files with 173 additions and 96 deletions

View File

@ -41,7 +41,7 @@ func init() {
flag.BoolVar(&debug, "debug", false, "show the version and exit") flag.BoolVar(&debug, "debug", false, "show the version and exit")
} }
func passphraseRetriever(keyName string, createNew bool, attempts int) (passphrase string, giveup bool, err error) { func passphraseRetriever(keyName, alias string, createNew bool, attempts int) (passphrase string, giveup bool, err error) {
//TODO(mccauley) Read from config once we have locked keys in notary-signer //TODO(mccauley) Read from config once we have locked keys in notary-signer
return "", false, nil; return "", false, nil;

View File

@ -89,7 +89,7 @@ func init() {
//TODO(mccauley): Appears unused? Remove it? Or is it here for early failure? //TODO(mccauley): Appears unused? Remove it? Or is it here for early failure?
privKeyStore, err = trustmanager.NewKeyFileStore(finalPrivDir, privKeyStore, err = trustmanager.NewKeyFileStore(finalPrivDir,
func (string, bool, int) (string, bool, error) { return "", false, nil}) func (string, string, bool, int) (string, bool, error) { return "", false, nil})
if err != nil { if err != nil {
fatalf("could not create KeyFileStore: %v", err) fatalf("could not create KeyFileStore: %v", err)
} }

View File

@ -16,11 +16,18 @@ import (
notaryclient "github.com/docker/notary/client" notaryclient "github.com/docker/notary/client"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
"github.com/docker/notary/trustmanager"
) )
// FIXME: This should not be hardcoded // FIXME: This should not be hardcoded
const hardcodedBaseURL = "https://notary-server:4443" const hardcodedBaseURL = "https://notary-server:4443"
var retriever trustmanager.PassphraseRetriever
func init() {
retriever = getNotaryPassphraseRetriever()
}
var remoteTrustServer string var remoteTrustServer string
var cmdTufList = &cobra.Command{ var cmdTufList = &cobra.Command{
@ -83,7 +90,7 @@ func tufAdd(cmd *cobra.Command, args []string) {
targetPath := args[2] targetPath := args[2]
repo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL, repo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL,
getInsecureTransport(), passphraseRetriever) getInsecureTransport(), retriever)
if err != nil { if err != nil {
fatalf(err.Error()) fatalf(err.Error())
} }
@ -108,7 +115,7 @@ func tufInit(cmd *cobra.Command, args []string) {
gun := args[0] gun := args[0]
nRepo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL, nRepo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL,
getInsecureTransport(), passphraseRetriever) getInsecureTransport(), retriever)
if err != nil { if err != nil {
fatalf(err.Error()) fatalf(err.Error())
} }
@ -144,7 +151,7 @@ func tufList(cmd *cobra.Command, args []string) {
} }
gun := args[0] gun := args[0]
repo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL, repo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL,
getInsecureTransport(), passphraseRetriever) getInsecureTransport(), retriever)
if err != nil { if err != nil {
fatalf(err.Error()) fatalf(err.Error())
} }
@ -170,7 +177,7 @@ func tufLookup(cmd *cobra.Command, args []string) {
targetName := args[1] targetName := args[1]
repo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL, repo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL,
getInsecureTransport(), passphraseRetriever) getInsecureTransport(), retriever)
if err != nil { if err != nil {
fatalf(err.Error()) fatalf(err.Error())
} }
@ -195,12 +202,12 @@ func tufPublish(cmd *cobra.Command, args []string) {
fmt.Println("Pushing changes to ", gun, ".") fmt.Println("Pushing changes to ", gun, ".")
repo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL, repo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL,
getInsecureTransport(), passphraseRetriever) getInsecureTransport(), retriever)
if err != nil { if err != nil {
fatalf(err.Error()) fatalf(err.Error())
} }
err = repo.Publish(passphraseRetriever) err = repo.Publish(retriever)
if err != nil { if err != nil {
fatalf(err.Error()) fatalf(err.Error())
} }
@ -241,7 +248,7 @@ func verify(cmd *cobra.Command, args []string) {
gun := args[0] gun := args[0]
targetName := args[1] targetName := args[1]
repo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL, repo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL,
getInsecureTransport(), passphraseRetriever) getInsecureTransport(), retriever)
if err != nil { if err != nil {
fatalf(err.Error()) fatalf(err.Error())
} }
@ -265,51 +272,82 @@ func verify(cmd *cobra.Command, args []string) {
return return
} }
func passphraseRetriever(keyName string, createNew bool, numAttempts int) (string, bool, error) { func getNotaryPassphraseRetriever() (trustmanager.PassphraseRetriever) {
fmt.Printf("Retrieving passphrase for key %s: ", keyName) userEnteredTargetsSnapshotsPass := false
targetsSnapshotsPass := ""
if (numAttempts > 3 && !createNew) { return func(keyID string, alias string, createNew bool, numAttempts int) (string, bool, error) {
return "", true, errors.New("Too many attempts") fmt.Printf("userEnteredTargetsSnapshotsPass: %s\n", userEnteredTargetsSnapshotsPass)
fmt.Printf("targetsSnapshotsPass: %s\n", targetsSnapshotsPass)
fmt.Printf("keyID: %s\n", keyID)
fmt.Printf("alias: %s\n", alias)
fmt.Printf("numAttempts: %s\n", numAttempts)
if numAttempts == 0 && userEnteredTargetsSnapshotsPass && (alias == "snapshot" || alias == "targets") {
fmt.Println("return cached value")
return targetsSnapshotsPass, false, nil;
}
if (numAttempts > 3 && !createNew) {
return "", true, errors.New("Too many attempts")
}
state, err := term.SaveState(0)
if err != nil {
return "", false, err
}
term.DisableEcho(0, state)
defer term.RestoreTerminal(0, state)
stdin := bufio.NewReader(os.Stdin)
if createNew {
fmt.Printf("Enter passphrase for new %s key with id %s: ", alias, keyID)
}else {
fmt.Printf("Enter key passphrase for %s key with id %s: ", alias, keyID)
}
passphrase, err := stdin.ReadBytes('\n')
fmt.Println()
if err != nil {
return "", false, err
}
passphrase = passphrase[0 : len(passphrase)-1]
if !createNew {
retPass := string(passphrase)
if alias == "snapshot" || alias == "targets" {
userEnteredTargetsSnapshotsPass = true
targetsSnapshotsPass = retPass
}
return string(passphrase), false, nil;
}
if len(passphrase) < 8 {
fmt.Println("Please use a password manager to generate and store a good random passphrase.")
return "", false, errors.New("Passphrase too short")
}
fmt.Printf("Repeat passphrase for new %s key with id %s:: ", alias, keyID)
confirmation, err := stdin.ReadBytes('\n')
fmt.Println()
if err != nil {
return "", false, err
}
confirmation = confirmation[0 : len(confirmation)-1]
if !bytes.Equal(passphrase, confirmation) {
return "", false, errors.New("The entered passphrases do not match")
}
retPass := string(passphrase)
if alias == "snapshots" || alias == "targets" {
userEnteredTargetsSnapshotsPass = true
targetsSnapshotsPass = retPass
}
return retPass, false, nil
} }
state, err := term.SaveState(0)
if err != nil {
return "", false, err
}
term.DisableEcho(0, state)
defer term.RestoreTerminal(0, state)
stdin := bufio.NewReader(os.Stdin)
fmt.Printf("Enter %s key passphrase: ", keyName)
passphrase, err := stdin.ReadBytes('\n')
fmt.Println()
if err != nil {
return "", false, err
}
passphrase = passphrase[0 : len(passphrase)-1]
if !createNew {
return string(passphrase), false, nil;
}
if len(passphrase) < 8 {
fmt.Println("Please use a password manager to generate and store a good random passphrase.")
return "", false, errors.New("Passphrase too short")
}
fmt.Printf("Repeat %s key passphrase: ", keyName)
confirmation, err := stdin.ReadBytes('\n')
fmt.Println()
if err != nil {
return "", false, err
}
confirmation = confirmation[0 : len(confirmation)-1]
if !bytes.Equal(passphrase, confirmation) {
return "", false, errors.New("The entered passphrases do not match")
}
return string(passphrase), false, nil
} }
func getInsecureTransport() *http.Transport { func getInsecureTransport() *http.Transport {

View File

@ -58,8 +58,8 @@ func (ccs *CryptoService) Create(role string, algorithm data.KeyAlgorithm) (data
} }
logrus.Debugf("generated new %s key for role: %s and keyID: %s", algorithm, role, privKey.ID()) logrus.Debugf("generated new %s key for role: %s and keyID: %s", algorithm, role, privKey.ID())
// Store the private key into our keystore with the name being: /GUN/ID.key // Store the private key into our keystore with the name being: /GUN/ID.key with an alias of role
err = ccs.keyStore.AddKey(filepath.Join(ccs.gun, privKey.ID()), privKey) err = ccs.keyStore.AddKey(filepath.Join(ccs.gun, privKey.ID()), role, privKey)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to add key to filestore: %v", err) return nil, fmt.Errorf("failed to add key to filestore: %v", err)
} }

View File

@ -34,6 +34,10 @@ var (
ErrNoKeysFoundForGUN = errors.New("no keys found for specified GUN") ErrNoKeysFoundForGUN = errors.New("no keys found for specified GUN")
) )
const (
aliasSuffix = ".alias"
)
// ExportRootKey exports the specified root key to an io.Writer in PEM format. // ExportRootKey exports the specified root key to an io.Writer in PEM format.
// The key's existing encryption is preserved. // The key's existing encryption is preserved.
func (km *KeyStoreManager) ExportRootKey(dest io.Writer, keyID string) error { func (km *KeyStoreManager) ExportRootKey(dest io.Writer, keyID string) error {
@ -89,29 +93,17 @@ func moveKeys(oldKeyStore, newKeyStore *trustmanager.KeyFileStore) error {
relKeyPath := strings.TrimPrefix(fullKeyPath, oldKeyStore.BaseDir()) relKeyPath := strings.TrimPrefix(fullKeyPath, oldKeyStore.BaseDir())
relKeyPath = strings.TrimPrefix(relKeyPath, string(filepath.Separator)) relKeyPath = strings.TrimPrefix(relKeyPath, string(filepath.Separator))
pemBytes, err := oldKeyStore.Get(relKeyPath) pemBytes, err := oldKeyStore.GetKey(relKeyPath)
if err != nil { if err != nil {
return err return err
} }
block, _ := pem.Decode(pemBytes) alias, err := oldKeyStore.GetKeyAlias(relKeyPath)
if block == nil { if err != nil {
return ErrNoValidPrivateKey return err
} }
if !x509.IsEncryptedPEMBlock(block) { err = newKeyStore.AddKey(relKeyPath, alias, pemBytes)
// Key is not encrypted. Parse it, and add it
// to the temporary store as an encrypted key.
privKey, err := trustmanager.ParsePEMPrivateKey(pemBytes, "")
if err != nil {
return err
}
err = newKeyStore.AddKey(relKeyPath, privKey)
} else {
// Encrypted key - pass it through without
// decrypting
err = newKeyStore.Add(relKeyPath, pemBytes)
}
if err != nil { if err != nil {
return err return err
@ -207,11 +199,13 @@ func (km *KeyStoreManager) ImportKeysZip(zipReader zip.Reader, passphrase string
// store in an inconsistent state // store in an inconsistent state
newRootKeys := make(map[string][]byte) newRootKeys := make(map[string][]byte)
newNonRootKeys := make(map[string]data.PrivateKey) newNonRootKeys := make(map[string]data.PrivateKey)
newNonRootKeyAliases := make(map[string]string)
// Note that using / as a separator is okay here - the zip package // Note that using / as a separator is okay here - the zip package
// guarantees that the separator will be / // guarantees that the separator will be /
rootKeysPrefix := privDir + "/" + rootKeysSubdir + "/" rootKeysPrefix := privDir + "/" + rootKeysSubdir + "/"
nonRootKeysPrefix := privDir + "/" + nonRootKeysSubdir + "/" nonRootKeysPrefix := privDir + "/" + nonRootKeysSubdir + "/"
aliasSuffix := ".alias"
// Iterate through the files in the archive. Don't add the keys // Iterate through the files in the archive. Don't add the keys
for _, f := range zipReader.File { for _, f := range zipReader.File {
@ -222,7 +216,7 @@ func (km *KeyStoreManager) ImportKeysZip(zipReader zip.Reader, passphrase string
return err return err
} }
pemBytes, err := ioutil.ReadAll(rc) fileBytes, err := ioutil.ReadAll(rc)
if err != nil { if err != nil {
return nil return nil
} }
@ -231,25 +225,29 @@ func (km *KeyStoreManager) ImportKeysZip(zipReader zip.Reader, passphrase string
// Note that using / as a separator is okay here - the zip // Note that using / as a separator is okay here - the zip
// package guarantees that the separator will be / // package guarantees that the separator will be /
if strings.HasPrefix(fNameTrimmed, rootKeysPrefix) { if strings.HasPrefix(fNameTrimmed, rootKeysPrefix) {
if err = checkRootKeyIsEncrypted(pemBytes); err != nil { if err = checkRootKeyIsEncrypted(fileBytes); err != nil {
rc.Close() rc.Close()
return err return err
} }
// Root keys are preserved without decrypting // Root keys are preserved without decrypting
keyName := strings.TrimPrefix(fNameTrimmed, rootKeysPrefix) keyName := strings.TrimPrefix(fNameTrimmed, rootKeysPrefix)
newRootKeys[keyName] = pemBytes newRootKeys[keyName] = fileBytes
} else if strings.HasPrefix(fNameTrimmed, nonRootKeysPrefix) { } else if strings.HasPrefix(fNameTrimmed, nonRootKeysPrefix) {
// Non-root keys need to be decrypted // Non-root keys need to be decrypted
key, err := trustmanager.ParsePEMPrivateKey(pemBytes, passphrase) key, err := trustmanager.ParsePEMPrivateKey(fileBytes, passphrase)
if err != nil { if err != nil {
rc.Close() rc.Close()
return err return err
} }
keyName := strings.TrimPrefix(fNameTrimmed, nonRootKeysPrefix) keyName := strings.TrimPrefix(fNameTrimmed, nonRootKeysPrefix)
newNonRootKeys[keyName] = key newNonRootKeys[keyName] = key
} else if strings.HasSuffix(fNameTrimmed, aliasSuffix) {
// Aliases need to be recorded so they can be reintroduced in the new zip
fileName := strings.TrimSuffix(fNameTrimmed, aliasSuffix)
newNonRootKeyAliases[fileName] = string(fileBytes)
} else { } else {
// This path inside the zip archive doesn't look like a // This path inside the zip archive doesn't look like a
// root key or a non-root key. To avoid adding a file // root key, non-root key, or alias. To avoid adding a file
// to the filestore that we won't be able to use, skip // to the filestore that we won't be able to use, skip
// this file in the import. // this file in the import.
logrus.Warnf("skipping import of key with a path that doesn't begin with %s or %s: %s", rootKeysPrefix, nonRootKeysPrefix, f.Name) logrus.Warnf("skipping import of key with a path that doesn't begin with %s or %s: %s", rootKeysPrefix, nonRootKeysPrefix, f.Name)
@ -261,13 +259,17 @@ func (km *KeyStoreManager) ImportKeysZip(zipReader zip.Reader, passphrase string
} }
for keyName, pemBytes := range newRootKeys { for keyName, pemBytes := range newRootKeys {
if err := km.rootKeyStore.Add(keyName + "." + aliasSuffix, []byte("root")); err != nil {
return err
}
if err := km.rootKeyStore.Add(keyName, pemBytes); err != nil { if err := km.rootKeyStore.Add(keyName, pemBytes); err != nil {
return err return err
} }
} }
for keyName, privKey := range newNonRootKeys { for keyName, privKey := range newNonRootKeys {
if err := km.nonRootKeyStore.AddKey(keyName, privKey); err != nil { alias := newNonRootKeyAliases[keyName]
if err := km.nonRootKeyStore.AddKey(keyName, alias, privKey); err != nil {
return err return err
} }
} }
@ -292,6 +294,11 @@ func moveKeysByGUN(oldKeyStore, newKeyStore *trustmanager.KeyFileStore, gun stri
return err return err
} }
alias, err := oldKeyStore.GetKeyAlias(relKeyPath)
if err != nil {
return err
}
block, _ := pem.Decode(pemBytes) block, _ := pem.Decode(pemBytes)
if block == nil { if block == nil {
return ErrNoValidPrivateKey return ErrNoValidPrivateKey
@ -307,7 +314,7 @@ func moveKeysByGUN(oldKeyStore, newKeyStore *trustmanager.KeyFileStore, gun stri
if err != nil { if err != nil {
return err return err
} }
err = newKeyStore.AddKey(relKeyPath, privKey) err = newKeyStore.AddKey(relKeyPath, alias, privKey)
if err != nil { if err != nil {
return err return err
} }

View File

@ -163,7 +163,7 @@ func (km *KeyStoreManager) GenRootKey(algorithm string) (string, error) {
} }
// Changing the root // Changing the root
km.rootKeyStore.AddKey(privKey.ID(), privKey) km.rootKeyStore.AddKey(privKey.ID(), "root", privKey)
return privKey.ID(), nil return privKey.ID(), nil
} }

View File

@ -9,14 +9,16 @@ import (
const ( const (
keyExtension = "key" keyExtension = "key"
aliasExtension = "alias"
) )
// KeyStore is a generic interface for private key storage // KeyStore is a generic interface for private key storage
type KeyStore interface { type KeyStore interface {
LimitedFileStore LimitedFileStore
AddKey(name string, privKey data.PrivateKey) error AddKey(name, alias string, privKey data.PrivateKey) error
GetKey(name string) (data.PrivateKey, error) GetKey(name string) (data.PrivateKey, error)
GetKeyAlias(name string) (string, error)
ListKeys() []string ListKeys() []string
RemoveKey(name string) error RemoveKey(name string) error
} }
@ -25,7 +27,7 @@ type KeyStore interface {
// for a given named key. If it should be treated as new passphrase (e.g. with // for a given named key. If it should be treated as new passphrase (e.g. with
// confirmation), createNew will be true. Attempts is passed in so that implementers // confirmation), createNew will be true. Attempts is passed in so that implementers
// decide how many chances to give to a human, for example. // decide how many chances to give to a human, for example.
type PassphraseRetriever func(keyName string, createNew bool, attempts int) (passphrase string, giveup bool, err error) type PassphraseRetriever func(keyId, alias string, createNew bool, attempts int) (passphrase string, giveup bool, err error)
// KeyFileStore persists and manages private keys on disk // KeyFileStore persists and manages private keys on disk
type KeyFileStore struct { type KeyFileStore struct {
@ -51,8 +53,8 @@ func NewKeyFileStore(baseDir string, passphraseRetriever PassphraseRetriever) (*
} }
// AddKey stores the contents of a PEM-encoded private key as a PEM block // AddKey stores the contents of a PEM-encoded private key as a PEM block
func (s *KeyFileStore) AddKey(name string, privKey data.PrivateKey) error { func (s *KeyFileStore) AddKey(name, alias string, privKey data.PrivateKey) error {
return addKey(s, s.PassphraseRetriever, name, privKey) return addKey(s, s.PassphraseRetriever, name, alias, privKey)
} }
// GetKey returns the PrivateKey given a KeyID // GetKey returns the PrivateKey given a KeyID
@ -60,6 +62,12 @@ func (s *KeyFileStore) GetKey(name string) (data.PrivateKey, error) {
return getKey(s, s.PassphraseRetriever, name) return getKey(s, s.PassphraseRetriever, name)
} }
// GetKeyAlias returns the PrivateKey given a KeyID
func (s *KeyFileStore) GetKeyAlias(name string) (string, error) {
return getKeyAlias(s, name)
}
// ListKeys returns a list of unique PublicKeys present on the KeyFileStore. // ListKeys returns a list of unique PublicKeys present on the KeyFileStore.
// There might be symlinks associating Certificate IDs to Public Keys, so this // There might be symlinks associating Certificate IDs to Public Keys, so this
// method only returns the IDs that aren't symlinks // method only returns the IDs that aren't symlinks
@ -69,7 +77,7 @@ func (s *KeyFileStore) ListKeys() []string {
// RemoveKey removes the key from the keyfilestore // RemoveKey removes the key from the keyfilestore
func (s *KeyFileStore) RemoveKey(name string) error { func (s *KeyFileStore) RemoveKey(name string) error {
return remove(s, name) return removeKey(s, name)
} }
// NewKeyMemoryStore returns a new KeyMemoryStore which holds keys in memory // NewKeyMemoryStore returns a new KeyMemoryStore which holds keys in memory
@ -80,8 +88,8 @@ func NewKeyMemoryStore(passphraseRetriever PassphraseRetriever) *KeyMemoryStore
} }
// AddKey stores the contents of a PEM-encoded private key as a PEM block // AddKey stores the contents of a PEM-encoded private key as a PEM block
func (s *KeyMemoryStore) AddKey(name string, privKey data.PrivateKey) error { func (s *KeyMemoryStore) AddKey(name, alias string, privKey data.PrivateKey) error {
return addKey(s, s.PassphraseRetriever, name, privKey) return addKey(s, s.PassphraseRetriever, name, alias, privKey)
} }
// GetKey returns the PrivateKey given a KeyID // GetKey returns the PrivateKey given a KeyID
@ -89,6 +97,12 @@ func (s *KeyMemoryStore) GetKey(name string) (data.PrivateKey, error) {
return getKey(s, s.PassphraseRetriever, name) return getKey(s, s.PassphraseRetriever, name)
} }
// GetKeyAlias returns the PrivateKey given a KeyID
func (s *KeyMemoryStore) GetKeyAlias(name string) (string, error) {
return getKeyAlias(s, name)
}
// ListKeys returns a list of unique PublicKeys present on the KeyFileStore. // ListKeys returns a list of unique PublicKeys present on the KeyFileStore.
// There might be symlinks associating Certificate IDs to Public Keys, so this // There might be symlinks associating Certificate IDs to Public Keys, so this
// method only returns the IDs that aren't symlinks // method only returns the IDs that aren't symlinks
@ -98,11 +112,11 @@ func (s *KeyMemoryStore) ListKeys() []string {
// RemoveKey removes the key from the keystore // RemoveKey removes the key from the keystore
func (s *KeyMemoryStore) RemoveKey(name string) error { func (s *KeyMemoryStore) RemoveKey(name string) error {
return remove(s, name) return removeKey(s, name)
} }
func addKey(s LimitedFileStore, passphraseRetriever PassphraseRetriever, name string, privKey data.PrivateKey) error { func addKey(s LimitedFileStore, passphraseRetriever PassphraseRetriever, name, alias string, privKey data.PrivateKey) error {
pemPrivKey, err := KeyToPEM(privKey) pemPrivKey, err := KeyToPEM(privKey)
if err != nil { if err != nil {
return err return err
@ -111,8 +125,8 @@ func addKey(s LimitedFileStore, passphraseRetriever PassphraseRetriever, name st
attempts := 0 attempts := 0
passphrase := "" passphrase := ""
giveup := false giveup := false
for (true) { for {
passphrase, giveup, err = passphraseRetriever(name, true, attempts) passphrase, giveup, err = passphraseRetriever(name, alias, true, attempts)
if err != nil { if err != nil {
attempts++ attempts++
continue continue
@ -124,17 +138,29 @@ func addKey(s LimitedFileStore, passphraseRetriever PassphraseRetriever, name st
} }
if passphrase != "" { if passphrase != "" {
encryptedPrivKey, err := EncryptPrivateKey(privKey, passphrase) pemPrivKey, err = EncryptPrivateKey(privKey, passphrase)
if err != nil { if err != nil {
return err return err
} }
return s.Add(name, encryptedPrivKey)
} }
err = s.Add(name + "." + aliasExtension, []byte(alias))
if err != nil {
return err
}
return s.Add(name, pemPrivKey) return s.Add(name, pemPrivKey)
} }
func getKeyAlias(s LimitedFileStore, name string) (string, error) {
keyAlias, err := s.Get(name + "." + aliasExtension)
if err != nil {
return "", err
}
return string(keyAlias), nil
}
// GetKey returns the PrivateKey given a KeyID // GetKey returns the PrivateKey given a KeyID
func getKey(s LimitedFileStore, passphraseRetriever PassphraseRetriever, name string) (data.PrivateKey, error) { func getKey(s LimitedFileStore, passphraseRetriever PassphraseRetriever, name string) (data.PrivateKey, error) {
keyBytes, err := s.Get(name) keyBytes, err := s.Get(name)
@ -142,13 +168,19 @@ func getKey(s LimitedFileStore, passphraseRetriever PassphraseRetriever, name st
return nil, err return nil, err
} }
keyAlias, err := getKeyAlias(s, name)
if err != nil {
return nil, err
}
// See if the key is encrypted. If its encrypted we'll fail to parse the private key // See if the key is encrypted. If its encrypted we'll fail to parse the private key
privKey, err := ParsePEMPrivateKey(keyBytes, "") privKey, err := ParsePEMPrivateKey(keyBytes, "")
if err != nil { if err != nil {
// We need to decrypt the key, lets get a passphrase // We need to decrypt the key, lets get a passphrase
attempts := 0 attempts := 0
for (true) { for {
passphrase, giveup, err := passphraseRetriever(name, false, attempts) passphrase, giveup, err := passphraseRetriever(name, string(keyAlias), false, attempts)
// Check if the passphrase retriever got an error or if it is telling us to give up // Check if the passphrase retriever got an error or if it is telling us to give up
if giveup || err != nil { if giveup || err != nil {
return nil, err return nil, err
@ -179,6 +211,6 @@ func listKeys(s LimitedFileStore) []string {
} }
// RemoveKey removes the key from the keyfilestore // RemoveKey removes the key from the keyfilestore
func remove(s LimitedFileStore, name string) error { func removeKey(s LimitedFileStore, name string) error {
return s.Remove(name) return s.Remove(name)
} }