rework export key

Signed-off-by: Riyaz Faizullabhoy <riyaz.faizullabhoy@docker.com>
This commit is contained in:
Riyaz Faizullabhoy 2016-02-02 14:21:19 -08:00
parent 3b3026c121
commit 12fd5aa246
3 changed files with 51 additions and 27 deletions

View File

@ -52,10 +52,10 @@ var cmdKeysBackupTemplate = usageTemplate{
Long: "Backs up all of your accessible of keys. The keys are reencrypted with a new passphrase. The output is a ZIP file. If the --gun option is passed, only signing keys and no root keys will be backed up. Does not work on keys that are only in hardware (e.g. Yubikeys).",
}
var cmdKeyExportRootTemplate = usageTemplate{
var cmdKeyExportTemplate = usageTemplate{
Use: "export [ keyID ] [ pemfilename ]",
Short: "Export a root key on disk to a PEM file.",
Long: "Exports a single root key on disk, without reencrypting. The output is a PEM file. Does not work on keys that are only in hardware (e.g. Yubikeys).",
Short: "Export a private key on disk to a PEM file.",
Long: "Exports a single private key on disk, without reencrypting. The output is a PEM file. Does not work on keys that are only in hardware (e.g. Yubikeys).",
}
var cmdKeysRestoreTemplate = usageTemplate{
@ -64,10 +64,10 @@ var cmdKeysRestoreTemplate = usageTemplate{
Long: "Restores one or more keys from a ZIP file. If hardware key storage (e.g. a Yubikey) is available, root keys will be imported into the hardware, but not backed up to disk in the same location as the other, non-root keys.",
}
var cmdKeyImportRootTemplate = usageTemplate{
var cmdKeyImportTemplate = usageTemplate{
Use: "import [ pemfilename ]",
Short: "Imports a root key from a PEM file.",
Long: "Imports a single root key from a PEM file. If a hardware key storage (e.g. Yubikey) is available, the root key will be imported into the hardware but not backed up on disk again.",
Short: "Imports a key from a PEM file.",
Long: "Imports a single key from a PEM file. If a hardware key storage (e.g. Yubikey) is available, the root key will be imported into the hardware but not backed up on disk again.",
}
var cmdKeyRemoveTemplate = usageTemplate{
@ -88,7 +88,7 @@ type keyCommander struct {
getRetriever func() passphrase.Retriever
// these are for command line parsing - no need to set
keysExportRootChangePassphrase bool
keysExportChangePassphrase bool
keysExportGUN string
rotateKeyRole string
rotateKeyServerManaged bool
@ -99,7 +99,8 @@ func (k *keyCommander) GetCommand() *cobra.Command {
cmd.AddCommand(cmdKeyListTemplate.ToCommand(k.keysList))
cmd.AddCommand(cmdKeyGenerateRootKeyTemplate.ToCommand(k.keysGenerateRootKey))
cmd.AddCommand(cmdKeysRestoreTemplate.ToCommand(k.keysRestore))
cmd.AddCommand(cmdKeyImportRootTemplate.ToCommand(k.keysImportRoot))
cmd.AddCommand(cmdKeyImportTemplate.ToCommand(k.keysImportRoot))
cmd.AddCommand(cmdKeyRemoveTemplate.ToCommand(k.keyRemove))
cmd.AddCommand(cmdKeyPasswdTemplate.ToCommand(k.keyPassphraseChange))
@ -108,11 +109,11 @@ func (k *keyCommander) GetCommand() *cobra.Command {
&k.keysExportGUN, "gun", "g", "", "Globally Unique Name to export keys for")
cmd.AddCommand(cmdKeysBackup)
cmdKeyExportRoot := cmdKeyExportRootTemplate.ToCommand(k.keysExportRoot)
cmdKeyExportRoot.Flags().BoolVarP(
&k.keysExportRootChangePassphrase, "change-passphrase", "p", false,
cmdKeyExport := cmdKeyExportTemplate.ToCommand(k.keysExport)
cmdKeyExport.Flags().BoolVarP(
&k.keysExportChangePassphrase, "change-passphrase", "p", false,
"Set a new passphrase for the key being exported")
cmd.AddCommand(cmdKeyExportRoot)
cmd.AddCommand(cmdKeyExport)
cmdRotateKey := cmdRotateKeyTemplate.ToCommand(k.keysRotate)
cmdRotateKey.Flags().BoolVarP(&k.rotateKeyServerManaged, "server-managed", "r",
@ -236,8 +237,8 @@ func (k *keyCommander) keysBackup(cmd *cobra.Command, args []string) error {
return nil
}
// keysExportRoot exports a root key by ID to a PEM file
func (k *keyCommander) keysExportRoot(cmd *cobra.Command, args []string) error {
// keysExport exports a key by ID to a PEM file
func (k *keyCommander) keysExport(cmd *cobra.Command, args []string) error {
if len(args) < 2 {
cmd.Usage()
return fmt.Errorf("Must specify key ID and output filename for export")
@ -247,7 +248,7 @@ func (k *keyCommander) keysExportRoot(cmd *cobra.Command, args []string) error {
exportFilename := args[1]
if len(keyID) != notary.Sha256HexSize {
return fmt.Errorf("Please specify a valid root key ID")
return fmt.Errorf("Please specify a valid key ID")
}
config, err := k.configGetter()
@ -258,24 +259,43 @@ func (k *keyCommander) keysExportRoot(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
cs := cryptoservice.NewCryptoService("", ks...)
// Search for this key, determine whether this key has a GUN
keyGun := ""
keyRole := ""
for _, store := range ks {
for keypath, role := range store.ListKeys() {
if filepath.Base(keypath) == keyID {
keyRole = role
if role != data.CanonicalRootRole {
dirPath := filepath.Dir(keypath)
if dirPath != "." { // no gun
keyGun = dirPath
}
}
break
}
}
}
cs := cryptoservice.NewCryptoService(keyGun, ks...)
exportFile, err := os.Create(exportFilename)
if err != nil {
return fmt.Errorf("Error creating output file: %v", err)
}
if k.keysExportRootChangePassphrase {
if k.keysExportChangePassphrase {
// Must use a different passphrase retriever to avoid caching the
// unlocking passphrase and reusing that.
exportRetriever := k.getRetriever()
err = cs.ExportRootKeyReencrypt(exportFile, keyID, exportRetriever)
err = cs.ExportKeyReencrypt(exportFile, keyID, exportRetriever)
} else {
err = cs.ExportRootKey(exportFile, keyID)
err = cs.ExportKey(exportFile, keyID, keyRole)
}
exportFile.Close()
if err != nil {
os.Remove(exportFilename)
return fmt.Errorf("Error exporting root key: %v", err)
return fmt.Errorf("Error exporting %s key: %v", keyRole, err)
}
return nil
}

View File

@ -13,6 +13,7 @@ import (
"github.com/docker/notary/passphrase"
"github.com/docker/notary/trustmanager"
"github.com/docker/notary/tuf/data"
)
const zipMadeByUNIX = 3 << 8
@ -31,15 +32,18 @@ var (
ErrNoKeysFoundForGUN = errors.New("no keys found for specified GUN")
)
// ExportRootKey exports the specified root key to an io.Writer in PEM format.
// ExportKey exports the specified key to an io.Writer in PEM format.
// The key's existing encryption is preserved.
func (cs *CryptoService) ExportRootKey(dest io.Writer, keyID string) error {
func (cs *CryptoService) ExportKey(dest io.Writer, keyID, role string) error {
var (
pemBytes []byte
err error
)
for _, ks := range cs.keyStores {
if role != data.CanonicalRootRole {
keyID = filepath.Join(cs.gun, keyID)
}
pemBytes, err = ks.ExportKey(keyID)
if err != nil {
continue
@ -59,9 +63,9 @@ func (cs *CryptoService) ExportRootKey(dest io.Writer, keyID string) error {
return nil
}
// ExportRootKeyReencrypt exports the specified root key to an io.Writer in
// ExportRootKeyReencrypt exports the specified private 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 {
func (cs *CryptoService) ExportKeyReencrypt(dest io.Writer, keyID string, newPassphraseRetriever passphrase.Retriever) error {
privateKey, role, err := cs.GetPrivateKey(keyID)
if err != nil {
return err
@ -110,7 +114,7 @@ func (cs *CryptoService) ImportRootKey(source io.Reader) error {
for _, ks := range cs.keyStores {
// don't redeclare err, we want the value carried out of the loop
if err = ks.ImportKey(pemBytes, "root"); err == nil {
if err = ks.ImportKey(pemBytes, data.CanonicalRootRole); err == nil {
return nil //bail on the first keystore we import to
}
}

View File

@ -284,7 +284,7 @@ func TestImportExportRootKey(t *testing.T) {
tempKeyFilePath := tempKeyFile.Name()
defer os.Remove(tempKeyFilePath)
err = cs.ExportRootKey(tempKeyFile, rootKeyID)
err = cs.ExportKey(tempKeyFile, rootKeyID, data.CanonicalRootRole)
assert.NoError(t, err)
tempKeyFile.Close()
@ -352,7 +352,7 @@ func TestImportExportRootKeyReencrypt(t *testing.T) {
tempKeyFilePath := tempKeyFile.Name()
defer os.Remove(tempKeyFilePath)
err = cs.ExportRootKeyReencrypt(tempKeyFile, rootKeyID, newPassphraseRetriever)
err = cs.ExportKeyReencrypt(tempKeyFile, rootKeyID, newPassphraseRetriever)
assert.NoError(t, err)
tempKeyFile.Close()