Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cmds): add cleartext PEM/PKCS8 for key import/export #8616

Merged
32 changes: 28 additions & 4 deletions core/commands/keystore.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,14 +153,21 @@ Exports a named libp2p key to disk.

By default, the output will be stored at './<key-name>.key', but an alternate
path can be specified with '--output=<path>' or '-o=<path>'.

It is possible to export a private key to interoperable PEM PKCS8 format by passing
explicit '--format=pem-pkcs8-cleartext'. Produced PEM file can then be consumed
by other software. For example, using openssl to get a PEM with public key:

$ ipfs key export testkey --format=pem-pkcs8-cleartext -o privkey.pem
$ openssl pkey -in privkey.pem -pubout > pubkey.pem
`,
},
Arguments: []cmds.Argument{
cmds.StringArg("name", true, false, "name of key to export").EnableStdin(),
},
Options: []cmds.Option{
cmds.StringOption(outputOptionName, "o", "The path where the output should be stored."),
cmds.StringOption(keyFormatOptionName, "f", "The format of the exported private key.").WithDefault(keyFormatLibp2pCleartextOption),
cmds.StringOption(keyFormatOptionName, "f", "The format of the exported private key, libp2p-protobuf-cleartext or pem-pkcs8-cleartext.").WithDefault(keyFormatLibp2pCleartextOption),
},
NoRemote: true,
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
Expand Down Expand Up @@ -296,10 +303,22 @@ path can be specified with '--output=<path>' or '-o=<path>'.
var keyImportCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Import a key and prints imported key id",
ShortDescription: `
Imports a key and stores it under provided name.

By default, the is assumed to be in 'libp2p-protobuf-cleartext' format,
however it is possible to import private keys wrapped in interoperable PEM PKCS8
by passing '--format=pem-pkcs8-cleartext'.

PEM format allows for key generation outside of IPFS node:

$ openssl genpkey -algorithm ED25519 > ed25519.pem
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have to add openssl as a dependency, but we could add a test fixture here that verifies we're compatible by dumping a private key into the sharness test folder (like we have for CAR import/export tests). We could then do some round-tripping tests to make sure everything is as expected (e.g. openssl -> import -> export as libp2p -> import -> export as pem -> check equality).

Hopefully should be relatively straightforward, but if not it's not strictly necessary.

$ ipfs key import test-openssl -f pem-pkcs8-cleartext ed25519.pem
Comment on lines +315 to +316
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This example is the best remainder of why we're doing this. It's pretty cool to be able to easily interoperate with openssl commands 👍

`,
},
Options: []cmds.Option{
ke.OptionIPNSBase,
cmds.StringOption(keyFormatOptionName, "f", "The format of the private key to import.").WithDefault(keyFormatLibp2pCleartextOption),
cmds.StringOption(keyFormatOptionName, "f", "The format of the private key to import, libp2p-protobuf-cleartext or pem-pkcs8-cleartext.").WithDefault(keyFormatLibp2pCleartextOption),
},
Arguments: []cmds.Argument{
cmds.StringArg("name", true, false, "name to associate with key in keychain"),
Expand Down Expand Up @@ -354,13 +373,18 @@ var keyImportCmd = &cmds.Command{

sk, _, err = crypto.KeyPairFromStdKey(stdKey)
if err != nil {
return fmt.Errorf("converting std Go key to libp2p key : %w", err)
return fmt.Errorf("converting std Go key to libp2p key: %w", err)

}
case keyFormatLibp2pCleartextOption:
sk, err = crypto.UnmarshalPrivateKey(data)
if err != nil {
return err
// check if data is PEM, if so, provide user with hint
pemBlock, _ := pem.Decode(data)
if pemBlock != nil {
return fmt.Errorf("unexpected PEM block for format=%s: try again with format=%s", keyFormatLibp2pCleartextOption, keyFormatPemCleartextOption)
}
return fmt.Errorf("unable to unmarshall format=%s: %w", keyFormatLibp2pCleartextOption, err)
}

default:
Expand Down
6 changes: 3 additions & 3 deletions test/sharness/t0165-keystore.sh
Original file line number Diff line number Diff line change
Expand Up @@ -213,10 +213,10 @@ test_key_import_export() {
KEY_NAME=$1
FORMAT=$2
ORIG_KEY="generated_$KEY_NAME"
if [ $FORMAT == "libp2p-protobuf-cleartext" ]; then
FILE_EXT="key"
else
if [ $FORMAT == "pem-pkcs8-cleartext" ]; then
FILE_EXT="pem"
else
FILE_EXT="key"
fi

test_expect_success "export and import $KEY_NAME with format $FORMAT" '
Expand Down