Add ec-curve parameter public key export/export-address subcommands (#3333)

* Logging curve name when creating a new key
* Add ec-curve parameter to export subcommand

Signed-off-by: Lucas Saldanha <lucascrsaldanha@gmail.com>
pull/3362/head
Lucas Saldanha 3 years ago committed by GitHub
parent 048dbb4824
commit a74ece7692
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 26
      besu/src/main/java/org/hyperledger/besu/cli/subcommands/PublicKeySubCommand.java
  3. 170
      besu/src/test/java/org/hyperledger/besu/cli/PublicKeySubCommandTest.java
  4. 6
      crypto/src/main/java/org/hyperledger/besu/crypto/KeyPairUtil.java
  5. 2
      crypto/src/main/java/org/hyperledger/besu/crypto/SignatureAlgorithmType.java

@ -1,5 +1,6 @@
# Changelog
## 22.1.0
- Add `--ec-curve` parameter to export/export-address public-key subcommands [#3333](https://github.com/hyperledger/besu/pull/3333)
### 22.1.0 Breaking Changes

@ -24,6 +24,8 @@ import org.hyperledger.besu.cli.options.stable.NodePrivateKeyFileOption;
import org.hyperledger.besu.cli.subcommands.PublicKeySubCommand.AddressSubCommand;
import org.hyperledger.besu.cli.subcommands.PublicKeySubCommand.ExportSubCommand;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.crypto.SignatureAlgorithmType;
import org.hyperledger.besu.ethereum.core.Util;
import java.io.BufferedWriter;
@ -97,6 +99,7 @@ public class PublicKeySubCommand implements Runnable {
@Override
public void run() {
configureEcCurve(ecCurve, parentCommand.spec.commandLine());
run(publicKeyExportFile, keyPair -> keyPair.getPublicKey().toString());
}
}
@ -126,6 +129,7 @@ public class PublicKeySubCommand implements Runnable {
@Override
public void run() {
configureEcCurve(ecCurve, parentCommand.spec.commandLine());
run(addressExportFile, keyPair -> Util.publicKeyToAddress(keyPair.getPublicKey()).toString());
}
}
@ -134,10 +138,20 @@ public class PublicKeySubCommand implements Runnable {
@SuppressWarnings("unused")
@ParentCommand
private PublicKeySubCommand parentCommand; // Picocli injects reference to parent command
protected PublicKeySubCommand parentCommand; // Picocli injects reference to parent command
@Mixin private final NodePrivateKeyFileOption nodePrivateKeyFileOption = null;
@Option(
names = "--ec-curve",
paramLabel = "<NAME>",
description =
"Elliptic curve to use when creating a new key (default: "
+ SignatureAlgorithmType.DEFAULT_EC_CURVE_NAME
+ ")",
arity = "0..1")
protected String ecCurve = null;
@Spec private final CommandSpec spec = null;
protected final void run(
@ -172,5 +186,15 @@ public class PublicKeySubCommand implements Runnable {
parentCommand.out.println(output);
}
}
protected static void configureEcCurve(final String ecCurve, final CommandLine commandLine) {
if (ecCurve != null) {
try {
SignatureAlgorithmFactory.setInstance(SignatureAlgorithmType.create(ecCurve));
} catch (IllegalArgumentException e) {
throw new CommandLine.ParameterException(commandLine, e.getMessage(), e);
}
}
}
}
}

@ -22,6 +22,7 @@ import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.NodeKey;
import org.hyperledger.besu.crypto.SECPPrivateKey;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.ethereum.core.Util;
import java.io.File;
@ -33,6 +34,7 @@ import org.apache.tuweni.bytes.Bytes32;
import org.bouncycastle.asn1.sec.SECNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -63,27 +65,35 @@ public class PublicKeySubCommandTest extends CommandTestAbstract {
+ System.lineSeparator();
private static final String EXPECTED_PUBLIC_KEY_EXPORT_USAGE =
"Usage: besu public-key export [-hV] [--node-private-key-file=<PATH>]"
"Usage: besu public-key export [-hV] [--ec-curve[=<NAME>]]"
+ System.lineSeparator()
+ " [--to=<FILE>]"
+ " [--node-private-key-file=<PATH>] [--to=<FILE>]"
+ System.lineSeparator()
+ "This command outputs the node public key. Default output is standard output."
+ System.lineSeparator()
+ " -h, --help Show this help message and exit."
+ " --ec-curve[=<NAME>] Elliptic curve to use when creating a new key"
+ System.lineSeparator()
+ " (default: secp256k1)"
+ System.lineSeparator()
+ " -h, --help Show this help message and exit."
+ System.lineSeparator()
+ " --node-private-key-file=<PATH>"
+ System.lineSeparator()
+ " The node's private key file (default: a file named \"key\" in"
+ " The node's private key file (default: a file named"
+ System.lineSeparator()
+ " the Besu data directory)"
+ " \"key\" in the Besu data directory)"
+ System.lineSeparator()
+ " --to=<FILE> File to write public key to instead of standard output"
+ " --to=<FILE> File to write public key to instead of standard"
+ System.lineSeparator()
+ " -V, --version Print version information and exit."
+ " output"
+ System.lineSeparator()
+ " -V, --version Print version information and exit."
+ System.lineSeparator();
private static final String EXPECTED_PUBLIC_KEY_EXPORT_ADDRESS_USAGE =
"Usage: besu public-key export-address [-hV] [--node-private-key-file=<PATH>]"
"Usage: besu public-key export-address [-hV] [--ec-curve[=<NAME>]]"
+ System.lineSeparator()
+ " [--node-private-key-file=<PATH>]"
+ System.lineSeparator()
+ " [--to=<FILE>]"
+ System.lineSeparator()
@ -91,17 +101,21 @@ public class PublicKeySubCommandTest extends CommandTestAbstract {
+ System.lineSeparator()
+ "output."
+ System.lineSeparator()
+ " -h, --help Show this help message and exit."
+ " --ec-curve[=<NAME>] Elliptic curve to use when creating a new key"
+ System.lineSeparator()
+ " (default: secp256k1)"
+ System.lineSeparator()
+ " -h, --help Show this help message and exit."
+ System.lineSeparator()
+ " --node-private-key-file=<PATH>"
+ System.lineSeparator()
+ " The node's private key file (default: a file named \"key\" in"
+ " The node's private key file (default: a file named"
+ System.lineSeparator()
+ " the Besu data directory)"
+ " \"key\" in the Besu data directory)"
+ System.lineSeparator()
+ " --to=<FILE> File to write address to instead of standard output"
+ " --to=<FILE> File to write address to instead of standard output"
+ System.lineSeparator()
+ " -V, --version Print version information and exit."
+ " -V, --version Print version information and exit."
+ System.lineSeparator();
private static final String PUBLIC_KEY_SUBCOMMAND_NAME = "public-key";
@ -117,6 +131,11 @@ public class PublicKeySubCommandTest extends CommandTestAbstract {
curve = new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH());
}
@Before
public void before() {
SignatureAlgorithmFactory.resetInstance();
}
// public-key sub-command
@Test
public void publicKeySubCommandExistsAndHasSubCommands() {
@ -323,4 +342,129 @@ public class PublicKeySubCommandTest extends CommandTestAbstract {
assertThat(commandErrorOutput.toString(UTF_8))
.startsWith("Private key cannot be loaded from file");
}
@Test
public void
callingPublicKeyExportSubCommandWithEcCurveNameCorrectlyConfiguresSignatureAlgorithmFactory()
throws Exception {
assertThat(SignatureAlgorithmFactory.isInstanceSet()).isFalse();
final File file = File.createTempFile("public", "key");
parseCommand(
PUBLIC_KEY_SUBCOMMAND_NAME,
PUBLIC_KEY_EXPORT_SUBCOMMAND_NAME,
"--to",
file.getPath(),
"--ec-curve",
CURVE_NAME);
assertThat(SignatureAlgorithmFactory.isInstanceSet()).isTrue();
assertThat(SignatureAlgorithmFactory.getInstance().getCurveName()).isEqualTo(CURVE_NAME);
}
@Test
public void
callingPublicKeyExportSubCommandWithoutEcCurveNameDoesNotConfiguresSignatureAlgorithmFactory()
throws Exception {
assertThat(SignatureAlgorithmFactory.isInstanceSet()).isFalse();
final File file = File.createTempFile("public", "key");
parseCommand(
PUBLIC_KEY_SUBCOMMAND_NAME, PUBLIC_KEY_EXPORT_SUBCOMMAND_NAME, "--to", file.getPath());
assertThat(SignatureAlgorithmFactory.isInstanceSet()).isFalse();
}
@Test
public void callingPublicKeyExportSubCommandWithInvalidEcCurveNameFails() throws Exception {
final File file = File.createTempFile("public", "key");
parseCommand(
PUBLIC_KEY_SUBCOMMAND_NAME,
PUBLIC_KEY_EXPORT_SUBCOMMAND_NAME,
"--to",
file.getPath(),
"--ec-curve",
"foo");
assertThat(commandErrorOutput.toString(UTF_8))
.contains("foo is not in the list of valid elliptic curves");
}
@Test
public void
callingPublicKeyExportAddressSubCommandWithEcCurveNameCorrectlyConfiguresSignatureAlgorithmFactory()
throws Exception {
assertThat(SignatureAlgorithmFactory.isInstanceSet()).isFalse();
final SECPPrivateKey privateKey =
SECPPrivateKey.create(
Bytes32.fromHexString(
"0x8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63"),
ALGORITHM);
final Path privateKeyFile = Files.createTempFile("private", "address");
Files.writeString(privateKeyFile, privateKey.toString());
parseCommand(
PUBLIC_KEY_SUBCOMMAND_NAME,
PUBLIC_KEY_EXPORT_ADDRESS_SUBCOMMAND_NAME,
"--node-private-key-file",
privateKeyFile.toString(),
"--ec-curve",
CURVE_NAME);
assertThat(SignatureAlgorithmFactory.isInstanceSet()).isTrue();
assertThat(SignatureAlgorithmFactory.getInstance().getCurveName()).isEqualTo(CURVE_NAME);
}
@Test
public void
callingPublicKeyExportAddressSubCommandWithoutEcCurveNameDoesNotConfiguresSignatureAlgorithmFactory()
throws Exception {
assertThat(SignatureAlgorithmFactory.isInstanceSet()).isFalse();
final SECPPrivateKey privateKey =
SECPPrivateKey.create(
Bytes32.fromHexString(
"0x8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63"),
ALGORITHM);
final Path privateKeyFile = Files.createTempFile("private", "address");
Files.writeString(privateKeyFile, privateKey.toString());
parseCommand(
PUBLIC_KEY_SUBCOMMAND_NAME,
PUBLIC_KEY_EXPORT_ADDRESS_SUBCOMMAND_NAME,
"--node-private-key-file",
privateKeyFile.toString());
assertThat(SignatureAlgorithmFactory.isInstanceSet()).isFalse();
}
@Test
public void callingPublicKeyExportAddressSubCommandWithInvalidEcCurveNameFails()
throws Exception {
final SECPPrivateKey privateKey =
SECPPrivateKey.create(
Bytes32.fromHexString(
"0x8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63"),
ALGORITHM);
final Path privateKeyFile = Files.createTempFile("private", "address");
Files.writeString(privateKeyFile, privateKey.toString());
parseCommand(
PUBLIC_KEY_SUBCOMMAND_NAME,
PUBLIC_KEY_EXPORT_ADDRESS_SUBCOMMAND_NAME,
"--node-private-key-file",
privateKeyFile.toString(),
"--ec-curve",
"foo");
assertThat(commandErrorOutput.toString(UTF_8))
.contains("foo is not in the list of valid elliptic curves");
}
}

@ -69,11 +69,13 @@ public class KeyPairUtil {
LOG.info(
"Loaded public key {} from {}", key.getPublicKey().toString(), keyFile.getAbsolutePath());
} else {
key = SIGNATURE_ALGORITHM.get().generateKeyPair();
final SignatureAlgorithm signatureAlgorithm = SIGNATURE_ALGORITHM.get();
key = signatureAlgorithm.generateKeyPair();
storeKeyFile(key, keyFile.getParentFile().toPath());
LOG.info(
"Generated new public key {} and stored it to {}",
"Generated new {} public key {} and stored it to {}",
signatureAlgorithm.getCurveName(),
key.getPublicKey().toString(),
keyFile.getAbsolutePath());
}

@ -22,7 +22,7 @@ import com.google.common.collect.ImmutableMap;
public class SignatureAlgorithmType {
private static final String DEFAULT_EC_CURVE_NAME = "secp256k1";
public static final String DEFAULT_EC_CURVE_NAME = "secp256k1";
private static final ImmutableMap<String, Supplier<SignatureAlgorithm>> SUPPORTED_ALGORITHMS =
ImmutableMap.of(DEFAULT_EC_CURVE_NAME, SECP256K1::new, "secp256r1", SECP256R1::new);

Loading…
Cancel
Save