diff --git a/convex-cli/src/main/java/convex/cli/Main.java b/convex-cli/src/main/java/convex/cli/Main.java index 6f62b4316..d60b478dc 100644 --- a/convex-cli/src/main/java/convex/cli/Main.java +++ b/convex-cli/src/main/java/convex/cli/Main.java @@ -41,6 +41,7 @@ import picocli.CommandLine.Command; import picocli.CommandLine.IExecutionExceptionHandler; import picocli.CommandLine.IVersionProvider; +import picocli.CommandLine.Mixin; import picocli.CommandLine.Option; import picocli.CommandLine.ParseResult; import picocli.CommandLine.ScopeType; @@ -68,17 +69,14 @@ public class Main extends ACommand { public CommandLine commandLine = new CommandLine(this); - @Option(names = { "--keystore" }, - defaultValue = "${env:CONVEX_KEYSTORE:-" + Constants.KEYSTORE_FILENAME+ "}", - scope = ScopeType.INHERIT, - description = "Keystore filename. Default: ${DEFAULT-VALUE}") - private String keyStoreFilename; + @Mixin + private StoreMixin storeMixin; @Option(names = { "-k","--key" }, defaultValue = "${env:CONVEX_KEY}", scope = ScopeType.INHERIT, description = "Public key to use. Default: ${DEFAULT-VALUE}") - private String keySpec; + public String publicKey; @Option(names = { "-p","--keypass" }, defaultValue = "${env:CONVEX_KEY_PASSWORD}", @@ -92,14 +90,7 @@ public class Main extends ACommand { description = "Apply strict security. Will forbid actions with dubious security implications.") private boolean paranoid; - /** - * Password for keystore. Option named to match Java keytool - */ - @Option(names = {"--storepass" }, - scope = ScopeType.INHERIT, - defaultValue = "${env:CONVEX_KEYSTORE_PASSWORD}", - description = "Password to read/write to the Keystore") - private String keystorePassword; + @Option(names = { "-n","--noninteractive" }, scope = ScopeType.INHERIT, @@ -219,7 +210,7 @@ public int handleExecutionException(Exception ex, CommandLine commandLine, Parse String msg=ce.getMessage(); informError(msg); Throwable cause = ce.getCause(); - if ((verbose>=2) && (cause != null)) { + if ((verbose>=3) && (cause != null)) { err.println("Underlying cause: "); cause.printStackTrace(err); } @@ -243,12 +234,12 @@ public int handleExecutionException(Exception ex, CommandLine commandLine, Parse public char[] getStorePassword() { char[] storepass = null; - if (this.keystorePassword != null) { - storepass = this.keystorePassword.toCharArray(); + if (storeMixin.keystorePassword != null) { + storepass = storeMixin.keystorePassword.toCharArray(); } else { if (!nonInteractive) { storepass = readPassword("Enter Keystore Password: "); - keystorePassword=new String(storepass); + storeMixin.keystorePassword=new String(storepass); } if (storepass == null) { @@ -288,20 +279,9 @@ public char[] getKeyPassword() { return keypass; } - /** - * Gets the keystore file name currently used for the CLI - * - * @return File name, or null if not specified - */ - public File getKeyStoreFile() { - if (keyStoreFilename != null) { - File f = Utils.getPath(keyStoreFilename); - return f; - } - return null; - } - private KeyStore keyStore = null; + KeyStore keyStore = null; + /** * Gets the current key store @@ -331,7 +311,7 @@ public KeyStore loadKeyStore() { * @return KeyStore instance, or null if does not exist */ public KeyStore loadKeyStore(boolean isCreate, char[] password) { - File keyFile = getKeyStoreFile(); + File keyFile = storeMixin.getKeyStoreFile(); try { if (keyFile.exists()) { keyStore = PFXTools.loadStore(keyFile, password); @@ -373,7 +353,7 @@ public AKeyPair loadKeyFromStore(String publicKey) { char[] storePassword = getStorePassword(); - File keyFile = getKeyStoreFile(); + File keyFile = storeMixin.getKeyStoreFile(); try { if (!keyFile.exists()) { throw new CLIError("Cannot find keystore file " + keyFile.getCanonicalPath()); @@ -450,15 +430,15 @@ public void saveKeyStore(char[] storePassword) { if (keyStore == null) throw new CLIError("Trying to save a keystore that has not been loaded!"); try { - PFXTools.saveStore(keyStore, getKeyStoreFile(), storePassword); + PFXTools.saveStore(keyStore, storeMixin.getKeyStoreFile(), storePassword); } catch (Throwable t) { throw Utils.sneakyThrow(t); } } public void saveKeyStore() { - if (keystorePassword==null) throw new CLIError("Key store password not provided"); - saveKeyStore(keystorePassword.toCharArray()); + if (storeMixin.keystorePassword==null) throw new CLIError("Key store password not provided"); + saveKeyStore(storeMixin.keystorePassword.toCharArray()); } public boolean isParanoid() { diff --git a/convex-cli/src/main/java/convex/cli/StoreMixin.java b/convex-cli/src/main/java/convex/cli/StoreMixin.java new file mode 100644 index 000000000..a285f45fc --- /dev/null +++ b/convex-cli/src/main/java/convex/cli/StoreMixin.java @@ -0,0 +1,39 @@ +package convex.cli; + +import java.io.File; + +import convex.core.util.Utils; +import picocli.CommandLine.Option; +import picocli.CommandLine.ScopeType; + +public class StoreMixin { + + @Option(names = { "--keystore" }, + defaultValue = "${env:CONVEX_KEYSTORE:-" + Constants.KEYSTORE_FILENAME+ "}", + scope = ScopeType.INHERIT, + description = "Keystore filename. Default: ${DEFAULT-VALUE}") + private String keyStoreFilename; + + /** + * Password for keystore. Option named to match Java keytool + */ + @Option(names = {"--storepass" }, + scope = ScopeType.INHERIT, + defaultValue = "${env:CONVEX_KEYSTORE_PASSWORD}", + description = "Password to read/write to the Keystore") + String keystorePassword; + + /** + * Gets the keystore file name currently used for the CLI + * + * @return File name, or null if not specified + */ + public File getKeyStoreFile() { + if (keyStoreFilename != null) { + File f = Utils.getPath(keyStoreFilename); + return f; + } + return null; + } + +} diff --git a/convex-cli/src/main/java/convex/cli/key/KeyExport.java b/convex-cli/src/main/java/convex/cli/key/KeyExport.java index 110a8b620..e9e460cc5 100644 --- a/convex-cli/src/main/java/convex/cli/key/KeyExport.java +++ b/convex-cli/src/main/java/convex/cli/key/KeyExport.java @@ -31,12 +31,6 @@ public class KeyExport extends AKeyCommand { @ParentCommand protected Key keyParent; - - @Option(names = {"--public-key" }, - description = "Hex string of the public key in the Keystore to use for the peer.%n" - + "You only need to enter in the first distinct hex values of the public key.%n" - + "For example: 0xf0234 or f0234") - private String keystorePublicKey ; @Option(names={"-o", "--output-file"}, description="Output file for the private key. Use '-' for STDOUT (default).") @@ -61,7 +55,7 @@ private void ensureExportPassword() { if (cli().isParanoid()) { throw new CLIError("Strict security: attempting to export PEM with no passphrase."); } else { - log.warn("No export password '--export-password' provided: Defaulting to blank."); + log.warn("No export passphrase '--export-password' provided: Defaulting to blank."); } exportPassword=""; } @@ -69,9 +63,15 @@ private void ensureExportPassword() { @Override public void run() { + String keystorePublicKey=cli().publicKey; if ((keystorePublicKey == null)||(keystorePublicKey.isEmpty())) { - log.warn("You need to provide at least --public-key parameter"); - return; + if (outputFilename==null) { + cli().inform("You must provide a --key parameter"); + showUsage(); + return; + } + + keystorePublicKey=cli().prompt("Enter public key to export: "); } String publicKey = keystorePublicKey; diff --git a/convex-cli/src/main/java/convex/cli/key/KeyImport.java b/convex-cli/src/main/java/convex/cli/key/KeyImport.java index 1f5c0b37d..250ccdfdd 100644 --- a/convex-cli/src/main/java/convex/cli/key/KeyImport.java +++ b/convex-cli/src/main/java/convex/cli/key/KeyImport.java @@ -96,7 +96,7 @@ public void run() { try { keyPair = PEMTools.decryptPrivateKeyFromPEM(importText, importPassphrase.toCharArray()); } catch (Exception e) { - throw new CLIError("Cannot decode PEM",e); + throw new CLIError("Cannot decode PEM. File may be corrupt or wrong passphrase used.",e); } } if (keyPair==null) throw new CLIError("Unable to import keypair"); diff --git a/convex-cli/src/main/java/convex/cli/peer/PeerCreate.java b/convex-cli/src/main/java/convex/cli/peer/PeerCreate.java index 808c71d37..44dd0ba7b 100644 --- a/convex-cli/src/main/java/convex/cli/peer/PeerCreate.java +++ b/convex-cli/src/main/java/convex/cli/peer/PeerCreate.java @@ -1,6 +1,5 @@ package convex.cli.peer; -import java.io.File; import java.security.KeyStore; import org.slf4j.Logger; @@ -87,11 +86,7 @@ public void run() { // save the new keypair in the keystore PFXTools.setKeyPair(keyStore, keyPair, mainParent.getKeyPassword()); - - File keyFile = mainParent.getKeyStoreFile(); - - // save the store to a file - PFXTools.saveStore(keyStore, keyFile, mainParent.getStorePassword()); + mainParent.saveKeyStore(); // connect using the default first user Convex convex = mainParent.connect(); diff --git a/convex-cli/src/test/java/convex/cli/key/KeyExportTest.java b/convex-cli/src/test/java/convex/cli/key/KeyExportTest.java index ce508d750..5fe116b02 100644 --- a/convex-cli/src/test/java/convex/cli/key/KeyExportTest.java +++ b/convex-cli/src/test/java/convex/cli/key/KeyExportTest.java @@ -19,7 +19,7 @@ public class KeyExportTest { private static final char[] KEYSTORE_PASSWORD = "testPassword".toCharArray(); - private static final char[] KEY_PASSWORD = "testKeytPassword".toCharArray(); + private static final char[] KEY_PASSWORD = "testKeyPassword".toCharArray(); private static final char[] EXPORT_PASSWORD = "testExportPassword".toCharArray(); private static final File TEMP_FILE; @@ -66,7 +66,7 @@ public void testKeyGenerateAndExport() throws Exception { "--storepass", new String(KEYSTORE_PASSWORD), "--keypass", new String(KEY_PASSWORD), "--keystore", KEYSTORE_FILENAME, - "--public-key", publicKey, + "--key", publicKey, "--export-password", new String(EXPORT_PASSWORD) ); String s=tester.getOutput(); @@ -83,7 +83,7 @@ public void testKeyGenerateAndExport() throws Exception { "--storepass", new String(KEYSTORE_PASSWORD), "--keypass", new String(KEY_PASSWORD), "--keystore", KEYSTORE_FILENAME, - "--public-key", publicKey + "--key", publicKey ); String s2=tester.getOutput(); assertEquals(ExitCodes.SUCCESS,tester.getResult());