mirror of https://github.com/docker/docs.git
keystore aliasing, take 1
Signed-off-by: Nathan McCauley <nathan.mccauley@docker.com>
This commit is contained in:
parent
7530774101
commit
5df1eb21f3
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue