diff --git a/cmd/ethkey/changepassphrase.go b/cmd/ethkey/changepassphrase.go new file mode 100644 index 0000000000..d1ae2ae0d8 --- /dev/null +++ b/cmd/ethkey/changepassphrase.go @@ -0,0 +1,72 @@ +package main + +import ( + "fmt" + "io/ioutil" + "strings" + + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/cmd/utils" + "gopkg.in/urfave/cli.v1" +) + +var newPassphraseFlag = cli.StringFlag{ + Name: "newpasswordfile", + Usage: "the file that contains the new passphrase for the keyfile", +} + +var commandChangePassphrase = cli.Command{ + Name: "changepassphrase", + Usage: "change the passphrase on a keyfile", + ArgsUsage: "", + Description: ` +Change the passphrase of a keyfile.`, + Flags: []cli.Flag{ + passphraseFlag, + newPassphraseFlag, + }, + Action: func(ctx *cli.Context) error { + keyfilepath := ctx.Args().First() + + // Read key from file. + keyjson, err := ioutil.ReadFile(keyfilepath) + if err != nil { + utils.Fatalf("Failed to read the keyfile at '%s': %v", keyfilepath, err) + } + + // Decrypt key with passphrase. + passphrase := getPassphrase(ctx) + key, err := keystore.DecryptKey(keyjson, passphrase) + if err != nil { + utils.Fatalf("Error decrypting key: %v", err) + } + + // Get a new passphrase. + fmt.Println("Please provide a new passphrase") + var newPhrase string + if passFile := ctx.String(newPassphraseFlag.Name); passFile != "" { + content, err := ioutil.ReadFile(passFile) + if err != nil { + utils.Fatalf("Failed to read new passphrase file '%s': %v", passFile, err) + } + newPhrase = strings.TrimRight(string(content), "\r\n") + } else { + newPhrase = promptPassphrase(true) + } + + // Encrypt the key with the new passphrase. + newJson, err := keystore.EncryptKey(key, newPhrase, keystore.StandardScryptN, keystore.StandardScryptP) + if err != nil { + utils.Fatalf("Error encrypting with new passphrase: %v", err) + } + + // Then write the new keyfile in place of the old one. + if err := ioutil.WriteFile(keyfilepath, newJson, 600); err != nil { + utils.Fatalf("Error writing new keyfile to disk: %v", err) + } + + // Don't print anything. Just return successfully, + // producing a positive exit code. + return nil + }, +} diff --git a/cmd/ethkey/generate.go b/cmd/ethkey/generate.go index 6d57d17fb4..fe9a0c1519 100644 --- a/cmd/ethkey/generate.go +++ b/cmd/ethkey/generate.go @@ -90,7 +90,7 @@ If you want to encrypt an existing private key, it can be specified by setting } // Encrypt key with passphrase. - passphrase := getPassPhrase(ctx, true) + passphrase := promptPassphrase(true) keyjson, err := keystore.EncryptKey(key, passphrase, keystore.StandardScryptN, keystore.StandardScryptP) if err != nil { utils.Fatalf("Error encrypting key: %v", err) diff --git a/cmd/ethkey/inspect.go b/cmd/ethkey/inspect.go index dbf5afc0ce..ba03d4d936 100644 --- a/cmd/ethkey/inspect.go +++ b/cmd/ethkey/inspect.go @@ -60,7 +60,7 @@ make sure to use this feature with great caution!`, } // Decrypt key with passphrase. - passphrase := getPassPhrase(ctx, false) + passphrase := getPassphrase(ctx) key, err := keystore.DecryptKey(keyjson, passphrase) if err != nil { utils.Fatalf("Error decrypting key: %v", err) diff --git a/cmd/ethkey/main.go b/cmd/ethkey/main.go index 4127f5566f..c434da0c05 100644 --- a/cmd/ethkey/main.go +++ b/cmd/ethkey/main.go @@ -38,6 +38,7 @@ func init() { app.Commands = []cli.Command{ commandGenerate, commandInspect, + commandChangePassphrase, commandSignMessage, commandVerifyMessage, } diff --git a/cmd/ethkey/message.go b/cmd/ethkey/message.go index 531a931c82..5caea69ff6 100644 --- a/cmd/ethkey/message.go +++ b/cmd/ethkey/message.go @@ -62,7 +62,7 @@ To sign a message contained in a file, use the --msgfile flag. } // Decrypt key with passphrase. - passphrase := getPassPhrase(ctx, false) + passphrase := getPassphrase(ctx) key, err := keystore.DecryptKey(keyjson, passphrase) if err != nil { utils.Fatalf("Error decrypting key: %v", err) diff --git a/cmd/ethkey/utils.go b/cmd/ethkey/utils.go index 0e563bf922..6f60ebaf1b 100644 --- a/cmd/ethkey/utils.go +++ b/cmd/ethkey/utils.go @@ -28,26 +28,14 @@ import ( "gopkg.in/urfave/cli.v1" ) -// getPassPhrase obtains a passphrase given by the user. It first checks the -// --passphrase command line flag and ultimately prompts the user for a -// passphrase. -func getPassPhrase(ctx *cli.Context, confirmation bool) string { - // Look for the --passphrase flag. - passphraseFile := ctx.String(passphraseFlag.Name) - if passphraseFile != "" { - content, err := ioutil.ReadFile(passphraseFile) - if err != nil { - utils.Fatalf("Failed to read passphrase file '%s': %v", - passphraseFile, err) - } - return strings.TrimRight(string(content), "\r\n") - } - - // Otherwise prompt the user for the passphrase. +// promptPassphrase prompts the user for a passphrase. Set confirmation to true +// to require the user to confirm the passphrase. +func promptPassphrase(confirmation bool) string { passphrase, err := console.Stdin.PromptPassword("Passphrase: ") if err != nil { utils.Fatalf("Failed to read passphrase: %v", err) } + if confirmation { confirm, err := console.Stdin.PromptPassword("Repeat passphrase: ") if err != nil { @@ -57,9 +45,29 @@ func getPassPhrase(ctx *cli.Context, confirmation bool) string { utils.Fatalf("Passphrases do not match") } } + return passphrase } +// getPassphrase obtains a passphrase given by the user. It first checks the +// --passfile command line flag and ultimately prompts the user for a +// passphrase. +func getPassphrase(ctx *cli.Context) string { + // Look for the --passwordfile flag. + passphraseFile := ctx.String(passphraseFlag.Name) + if passphraseFile != "" { + content, err := ioutil.ReadFile(passphraseFile) + if err != nil { + utils.Fatalf("Failed to read passphrase file '%s': %v", + passphraseFile, err) + } + return strings.TrimRight(string(content), "\r\n") + } + + // Otherwise prompt the user for the passphrase. + return promptPassphrase(false) +} + // signHash is a helper function that calculates a hash for the given message // that can be safely used to calculate a signature from. //