This is an automated email from the ASF dual-hosted git repository.
alopresto pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/master by this push:
new fdea4c5 NIFI-6026 - First commit which adds a new tls-toolkit mode
called Keystore. Should instead integrate the functionality into standalone
mode. NIFI-6026 - Updated splitKeystore to use standalone mode with a
-splitKeystore argument. NIFI-6026 - Removed unused file and references.
NIFI-6026 - Removed some code that is not necessary after doing some argument
checking in the command line parsing. NIFI-6026 - Made some small changes to
only require keystore password if keystore [...]
fdea4c5 is described below
commit fdea4c54dfb1b66073aa1ff603b5e814721ac5a7
Author: thenatog <[email protected]>
AuthorDate: Sun Feb 24 21:13:49 2019 -0500
NIFI-6026 - First commit which adds a new tls-toolkit mode called Keystore.
Should instead integrate the functionality into standalone mode.
NIFI-6026 - Updated splitKeystore to use standalone mode with a
-splitKeystore argument.
NIFI-6026 - Removed unused file and references.
NIFI-6026 - Removed some code that is not necessary after doing some
argument checking in the command line parsing.
NIFI-6026 - Made some small changes to only require keystore password if
keystore and key passwords are the same. Added some more tests.
NIFI-6026 - Added some more unit tests as per Andy's request. Also added a
check for empty keystores. Made tests a bit cleaner.
NIFI-6026 - Added empty keystore used by unit tests.
NIFI-6026 Fixed minor formatting and checkstyle issues.
This closes #3340.
Signed-off-by: Andy LoPresto <[email protected]>
---
.../src/test/resources/keystore.jks | Bin 0 -> 3088 bytes
.../tls/configuration/StandaloneConfig.java | 9 ++
.../tls/standalone/TlsToolkitStandalone.java | 59 ++++++--
.../TlsToolkitStandaloneCommandLine.java | 76 ++++++++---
.../apache/nifi/toolkit/tls/util/TlsHelper.java | 104 +++++++++++---
.../TlsToolkitStandaloneCommandLineTest.java | 149 ++++++++++++++++++---
.../nifi/toolkit/tls/util/TlsHelperTest.java | 147 +++++++++++++++-----
.../src/test/resources/empty-keystore.jks | Bin 0 -> 32 bytes
.../src/test/resources/keystore.jks | Bin 0 -> 3975 bytes
9 files changed, 437 insertions(+), 107 deletions(-)
diff --git a/nifi-commons/nifi-security-utils/src/test/resources/keystore.jks
b/nifi-commons/nifi-security-utils/src/test/resources/keystore.jks
new file mode 100644
index 0000000..246fe88
Binary files /dev/null and
b/nifi-commons/nifi-security-utils/src/test/resources/keystore.jks differ
diff --git
a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/StandaloneConfig.java
b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/StandaloneConfig.java
index d8e86347..4fd6af5 100644
---
a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/StandaloneConfig.java
+++
b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/StandaloneConfig.java
@@ -32,6 +32,7 @@ public class StandaloneConfig extends TlsConfig {
private List<String> clientPasswords;
private boolean clientPasswordsGenerated;
private boolean overwrite;
+ private boolean splitKeystore;
// TODO: A lot of these fields are null and cause NPEs in {@link
TlsToolkitStandalone} when not executed with expected input
@@ -90,4 +91,12 @@ public class StandaloneConfig extends TlsConfig {
public void setInstanceDefinitions(List<InstanceDefinition>
instanceDefinitions) {
this.instanceDefinitions = instanceDefinitions;
}
+
+ public void setSplitKeystore(boolean splitKeystore) {
+ this.splitKeystore = splitKeystore;
+ }
+
+ public boolean isSplitKeystore() {
+ return this.splitKeystore;
+ }
}
diff --git
a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandalone.java
b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandalone.java
index ffe4c5d..7407e5d 100644
---
a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandalone.java
+++
b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandalone.java
@@ -17,20 +17,6 @@
package org.apache.nifi.toolkit.tls.standalone;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.KeyStore;
-import java.security.SignatureException;
-import java.security.cert.Certificate;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.List;
import org.apache.nifi.security.util.CertificateUtils;
import org.apache.nifi.security.util.KeyStoreUtils;
import org.apache.nifi.security.util.KeystoreType;
@@ -50,6 +36,28 @@ import org.bouncycastle.util.io.pem.PemWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.security.GeneralSecurityException;
+import java.security.Key;
+import java.security.KeyPair;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SignatureException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
public class TlsToolkitStandalone {
public static final String NIFI_KEY = "nifi-key";
public static final String NIFI_CERT = "nifi-cert";
@@ -66,6 +74,28 @@ public class TlsToolkitStandalone {
this.outputStreamFactory = outputStreamFactory;
}
+ public void splitKeystore(StandaloneConfig standaloneConfig) throws
IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException,
UnrecoverableKeyException {
+ KeyStore keyStore = KeyStore.getInstance("JKS");
+ keyStore.load(new FileInputStream(standaloneConfig.getKeyStore()),
standaloneConfig.getKeyStorePassword().toCharArray());
+
+ if(keyStore.size() == 0) {
+ throw new KeyStoreException("Provided keystore " +
standaloneConfig.getKeyStore() + " was empty. No cert/key pairs to output to
file.");
+ }
+
+ if(standaloneConfig.getKeyPassword() == null ||
standaloneConfig.getKeyPassword().isEmpty()) {
+ splitKeystore(keyStore,
standaloneConfig.getKeyStorePassword().toCharArray(),
standaloneConfig.getBaseDir());
+ } else {
+ splitKeystore(keyStore,
standaloneConfig.getKeyPassword().toCharArray(), standaloneConfig.getBaseDir());
+ }
+ }
+
+ private void splitKeystore(KeyStore keyStore, char[] keyPassphrase, File
outputDirectory) throws KeyStoreException, UnrecoverableKeyException,
NoSuchAlgorithmException {
+ HashMap<String, Certificate> certificates =
TlsHelper.extractCerts(keyStore);
+ HashMap<String, Key> keys = TlsHelper.extractKeys(keyStore,
keyPassphrase);
+ TlsHelper.outputCertsAsPem(certificates, outputDirectory, ".crt");
+ TlsHelper.outputKeysAsPem(keys, outputDirectory, ".key");
+ }
+
public void createNifiKeystoresAndTrustStores(StandaloneConfig
standaloneConfig) throws GeneralSecurityException, IOException {
// TODO: This 200 line method should be refactored, as it is difficult
to test the various validations separately from the filesystem interaction and
generation logic
File baseDir = standaloneConfig.getBaseDir();
@@ -215,6 +245,7 @@ public class TlsToolkitStandalone {
tlsClientManager.addClientConfigurationWriter(new
NifiPropertiesTlsClientConfigWriter(niFiPropertiesWriterFactory, new
File(hostDir, "nifi.properties"),
hostname, instanceDefinition.getNumber()));
tlsClientManager.write(outputStreamFactory);
+
if (logger.isInfoEnabled()) {
logger.info("Successfully generated TLS configuration for " +
hostname + " " + hostIdentifierNumber + " in " + hostDir);
}
diff --git
a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneCommandLine.java
b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneCommandLine.java
index f11ad7b..f4c4d8b 100644
---
a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneCommandLine.java
+++
b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneCommandLine.java
@@ -17,18 +17,6 @@
package org.apache.nifi.toolkit.tls.standalone;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-import java.util.stream.Stream;
import org.apache.commons.cli.CommandLine;
import org.apache.nifi.toolkit.tls.commandLine.BaseTlsToolkitCommandLine;
import org.apache.nifi.toolkit.tls.commandLine.CommandLineParseException;
@@ -42,6 +30,19 @@ import org.apache.nifi.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
/**
* Command line parser for a StandaloneConfig object and a main entry point to
invoke the parser and run the standalone generator
*/
@@ -60,6 +61,7 @@ public class TlsToolkitStandaloneCommandLine extends
BaseTlsToolkitCommandLine {
public static final String NIFI_DN_SUFFIX_ARG = "nifiDnSuffix";
public static final String SUBJECT_ALTERNATIVE_NAMES_ARG =
"subjectAlternativeNames";
public static final String ADDITIONAL_CA_CERTIFICATE_ARG =
"additionalCACertificate";
+ public static final String SPLIT_KEYSTORE_ARG = "splitKeystore";
public static final String DEFAULT_OUTPUT_DIRECTORY =
calculateDefaultOutputDirectory(Paths.get("."));
@@ -86,10 +88,14 @@ public class TlsToolkitStandaloneCommandLine extends
BaseTlsToolkitCommandLine {
private List<String> clientPasswords;
private boolean clientPasswordsGenerated;
private boolean overwrite;
+ private boolean splitKeystore = false;
+ private String splitKeystoreFile;
private String dnPrefix;
private String dnSuffix;
private String domainAlternativeNames;
private String additionalCACertificatePath;
+ private String keyPassword;
+ private String keyStorePassword;
public TlsToolkitStandaloneCommandLine() {
this(new PasswordUtil());
@@ -113,6 +119,8 @@ public class TlsToolkitStandaloneCommandLine extends
BaseTlsToolkitCommandLine {
addOptionWithArg(null, NIFI_DN_SUFFIX_ARG, "String to append to
hostname(s) when determining DN.", TlsConfig.DEFAULT_DN_SUFFIX);
addOptionNoArg("O", OVERWRITE_ARG, "Overwrite existing host output.");
addOptionWithArg(null, ADDITIONAL_CA_CERTIFICATE_ARG, "Path to
additional CA certificate (used to sign toolkit CA certificate) in PEM format
if necessary");
+ addOptionWithArg(null, SPLIT_KEYSTORE_ARG, "Split out a given keystore
into its unencrypted key and certificates. Use -S and -K to specify the
keystore and key passwords.");
+
}
public static void main(String[] args) {
@@ -122,12 +130,24 @@ public class TlsToolkitStandaloneCommandLine extends
BaseTlsToolkitCommandLine {
} catch (CommandLineParseException e) {
System.exit(e.getExitCode().ordinal());
}
- try {
- new
TlsToolkitStandalone().createNifiKeystoresAndTrustStores(tlsToolkitStandaloneCommandLine.createConfig());
- } catch (Exception e) {
- tlsToolkitStandaloneCommandLine.printUsage("Error generating TLS
configuration. (" + e.getMessage() + ")");
- System.exit(ExitCode.ERROR_GENERATING_CONFIG.ordinal());
+
+ if(tlsToolkitStandaloneCommandLine.splitKeystore) {
+ StandaloneConfig conf =
tlsToolkitStandaloneCommandLine.createSplitKeystoreConfig();
+ try {
+ new TlsToolkitStandalone().splitKeystore(conf);
+ } catch (Exception e) {
+ tlsToolkitStandaloneCommandLine.printUsage("Error splitting
keystore. (" + e.getMessage() + ")");
+ System.exit(ExitCode.ERROR_GENERATING_CONFIG.ordinal());
+ }
+ } else {
+ try {
+ new
TlsToolkitStandalone().createNifiKeystoresAndTrustStores(tlsToolkitStandaloneCommandLine.createConfig());
+ } catch (Exception e) {
+ tlsToolkitStandaloneCommandLine.printUsage("Error generating
TLS configuration. (" + e.getMessage() + ")");
+ System.exit(ExitCode.ERROR_GENERATING_CONFIG.ordinal());
+ }
}
+
System.exit(ExitCode.SUCCESS.ordinal());
}
@@ -168,6 +188,17 @@ public class TlsToolkitStandaloneCommandLine extends
BaseTlsToolkitCommandLine {
clientPasswordsGenerated =
commandLine.getOptionValues(CLIENT_CERT_PASSWORD_ARG) == null;
overwrite = commandLine.hasOption(OVERWRITE_ARG);
+ if(commandLine.hasOption(SPLIT_KEYSTORE_ARG)) {
+ if(commandLine.hasOption(KEY_STORE_PASSWORD_ARG)) {
+ splitKeystoreFile =
commandLine.getOptionValue(SPLIT_KEYSTORE_ARG);
+ keyStorePassword =
commandLine.getOptionValue(KEY_STORE_PASSWORD_ARG);
+ keyPassword = commandLine.getOptionValue(KEY_PASSWORD_ARG);
+ splitKeystore = true;
+ } else {
+ printUsageAndThrow("-splitKeystore specified but no
keyStorePassword supplied.", ExitCode.ERROR_INCORRECT_NUMBER_OF_PASSWORDS);
+ }
+ }
+
additionalCACertificatePath =
commandLine.getOptionValue(ADDITIONAL_CA_CERTIFICATE_ARG);
String nifiPropertiesFile =
commandLine.getOptionValue(NIFI_PROPERTIES_FILE_ARG, "");
@@ -243,4 +274,15 @@ public class TlsToolkitStandaloneCommandLine extends
BaseTlsToolkitCommandLine {
return standaloneConfig;
}
+
+ public StandaloneConfig createSplitKeystoreConfig() {
+ StandaloneConfig splitKeystoreConfig = new StandaloneConfig();
+ splitKeystoreConfig.setBaseDir(baseDir);
+ splitKeystoreConfig.setKeyPassword(keyPassword);
+ splitKeystoreConfig.setKeyStorePassword(keyStorePassword);
+ splitKeystoreConfig.setKeyStore(splitKeystoreFile);
+ splitKeystoreConfig.setSplitKeystore(splitKeystore);
+
+ return splitKeystoreConfig;
+ }
}
diff --git
a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/util/TlsHelper.java
b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/util/TlsHelper.java
index 20bc2d9..3d6d5c5 100644
---
a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/util/TlsHelper.java
+++
b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/util/TlsHelper.java
@@ -17,26 +17,6 @@
package org.apache.nifi.toolkit.tls.util;
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.Reader;
-import java.io.StringReader;
-import java.io.StringWriter;
-import java.nio.charset.StandardCharsets;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.KeyStore;
-import java.security.NoSuchAlgorithmException;
-import java.security.PublicKey;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.List;
-import javax.crypto.Cipher;
-import javax.crypto.Mac;
-import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.DERNull;
@@ -63,6 +43,7 @@ import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaMiscPEMGenerator;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
+import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
@@ -72,6 +53,35 @@ import org.bouncycastle.util.io.pem.PemWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.Key;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+
public class TlsHelper {
private static final Logger logger =
LoggerFactory.getLogger(TlsHelper.class);
private static final int DEFAULT_MAX_ALLOWED_KEY_LENGTH = 128;
@@ -146,6 +156,60 @@ public class TlsHelper {
return password;
}
+ public static HashMap<String, Certificate> extractCerts(KeyStore keyStore)
throws KeyStoreException {
+ HashMap<String, Certificate> certs = new HashMap<>();
+ Enumeration<String> certAliases = keyStore.aliases();
+ while(certAliases.hasMoreElements()) {
+ String alias = certAliases.nextElement();
+ certs.put(alias, keyStore.getCertificate(alias));
+ }
+ return certs;
+ }
+
+ public static HashMap<String, Key> extractKeys(KeyStore keyStore, char[]
privKeyPass) throws KeyStoreException, UnrecoverableKeyException,
NoSuchAlgorithmException {
+ HashMap<String, Key> keys = new HashMap<>();
+ Enumeration<String> keyAliases = keyStore.aliases();
+ while(keyAliases.hasMoreElements()) {
+ String alias = keyAliases.nextElement();
+ Key key = keyStore.getKey(alias, privKeyPass);
+ if(key != null) {
+ keys.put(alias, key);
+ } else {
+ logger.warn("Key does not exist: Certificate with alias '" +
alias + "' had no private key.");
+ }
+ }
+ return keys;
+ }
+
+ public static void outputCertsAsPem(HashMap<String, Certificate> certs,
File directory, String extension) {
+ certs.forEach((String alias, Certificate cert)->{
+ try {
+ TlsHelper.outputAsPem(cert, alias, directory, extension);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ });
+ }
+
+ public static void outputKeysAsPem(HashMap<String, Key> keys, File
directory, String extension) {
+ keys.forEach((String alias, Key key) -> {
+ try {
+ TlsHelper.outputAsPem(key, alias, directory, extension);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ });
+ }
+
+ private static void outputAsPem(Object pemObj, String filename, File
directory, String extension) throws IOException {
+ OutputStream outputStream = new FileOutputStream(new File(directory,
TlsHelper.escapeFilename(filename) + extension));
+ OutputStreamWriter outputStreamWriter = new
OutputStreamWriter(outputStream);
+ JcaPEMWriter pemWriter = new JcaPEMWriter(outputStreamWriter);
+ JcaMiscPEMGenerator pemGen = new JcaMiscPEMGenerator(pemObj);
+ pemWriter.writeObject(pemGen);
+ pemWriter.close();
+ }
+
private static KeyPairGenerator createKeyPairGenerator(String algorithm,
int keySize) throws NoSuchAlgorithmException {
KeyPairGenerator instance = KeyPairGenerator.getInstance(algorithm);
instance.initialize(keySize);
diff --git
a/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneCommandLineTest.java
b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneCommandLineTest.java
index 0fe004a..18f2e40 100644
---
a/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneCommandLineTest.java
+++
b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneCommandLineTest.java
@@ -17,18 +17,15 @@
package org.apache.nifi.toolkit.tls.standalone;
-import org.apache.nifi.toolkit.tls.commandLine.CommandLineParseException;
-import org.apache.nifi.toolkit.tls.commandLine.ExitCode;
-import org.apache.nifi.toolkit.tls.configuration.InstanceDefinition;
-import org.apache.nifi.toolkit.tls.configuration.InstanceIdentifier;
-import org.apache.nifi.toolkit.tls.configuration.StandaloneConfig;
-import org.apache.nifi.toolkit.tls.configuration.TlsConfig;
-import org.apache.nifi.toolkit.tls.properties.NiFiPropertiesWriter;
-import org.apache.nifi.toolkit.tls.util.PasswordUtil;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.internal.stubbing.defaultanswers.ForwardsInvocations;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -36,7 +33,9 @@ import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.security.KeyStoreException;
import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -44,25 +43,44 @@ import java.util.Properties;
import java.util.Random;
import java.util.function.Function;
import java.util.stream.Collectors;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
+import org.apache.nifi.toolkit.tls.commandLine.CommandLineParseException;
+import org.apache.nifi.toolkit.tls.commandLine.ExitCode;
+import org.apache.nifi.toolkit.tls.configuration.InstanceDefinition;
+import org.apache.nifi.toolkit.tls.configuration.InstanceIdentifier;
+import org.apache.nifi.toolkit.tls.configuration.StandaloneConfig;
+import org.apache.nifi.toolkit.tls.configuration.TlsConfig;
+import org.apache.nifi.toolkit.tls.properties.NiFiPropertiesWriter;
+import org.apache.nifi.toolkit.tls.util.PasswordUtil;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.mockito.internal.stubbing.defaultanswers.ForwardsInvocations;
public class TlsToolkitStandaloneCommandLineTest {
private SecureRandom secureRandom;
private TlsToolkitStandaloneCommandLine tlsToolkitStandaloneCommandLine;
+
+ final String CHANGEIT = "changeit";
+ final String keyPass = CHANGEIT;
+ final String keystorePass = CHANGEIT;
+ final String wrongPass = "wrongpass";
+ private File outputFolder = null;
+ final String keystoreFile =
getClass().getClassLoader().getResource("keystore.jks").getFile();
+
+ @Rule
+ public TemporaryFolder tempFolder = new TemporaryFolder();
+
@Before
- public void setup() {
+ public void setup() throws IOException {
+
secureRandom = mock(SecureRandom.class);
doAnswer(new ForwardsInvocations(new
Random())).when(secureRandom).nextBytes(any(byte[].class));
tlsToolkitStandaloneCommandLine = new
TlsToolkitStandaloneCommandLine(new PasswordUtil(secureRandom));
+ outputFolder = tempFolder.newFolder("splitKeystoreOutputDir");
}
@Test
@@ -445,4 +463,91 @@ public class TlsToolkitStandaloneCommandLineTest {
properties.load(new
ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
return properties;
}
+
+ @Test
+ public void testSplitKeystore() throws Exception {
+ tlsToolkitStandaloneCommandLine.parse("-splitKeystore", keystoreFile,
"-S", keystorePass, "-K", keyPass, "-o", outputFolder.getPath());
+ StandaloneConfig standaloneConfig =
tlsToolkitStandaloneCommandLine.createSplitKeystoreConfig();
+
+ assertTrue(standaloneConfig.isSplitKeystore());
+ assertEquals(keyPass, standaloneConfig.getKeyPassword());
+ assertEquals(keystorePass, standaloneConfig.getKeyStorePassword());
+ TlsToolkitStandalone toolkit = new TlsToolkitStandalone();
+ toolkit.splitKeystore(standaloneConfig);
+
+ assertTrue(outputFolder.listFiles().length == 3);
+
+ // Validity checking of the output is done in TlsHelperTest
+ for(File file : outputFolder.listFiles()) {
+ assertTrue(file.length() > 0);
+ }
+ }
+
+ @Test(expected = CommandLineParseException.class)
+ public void testSplitKeystoreMissingPasswords() throws Exception {
+ tlsToolkitStandaloneCommandLine.parse("-splitKeystore", keystoreFile,
"-o", outputFolder.getPath());
+ StandaloneConfig standaloneConfig =
tlsToolkitStandaloneCommandLine.createSplitKeystoreConfig();
+
+ TlsToolkitStandalone toolkit = new TlsToolkitStandalone();
+ toolkit.splitKeystore(standaloneConfig);
+ }
+
+ @Test
+ public void testSplitKeystoreWithSameKeystoreAndKeyPassword() throws
Exception {
+ tlsToolkitStandaloneCommandLine.parse("-splitKeystore", keystoreFile,
"-S", keystorePass, "-o", outputFolder.getPath());
+ StandaloneConfig standaloneConfig =
tlsToolkitStandaloneCommandLine.createSplitKeystoreConfig();
+
+ TlsToolkitStandalone toolkit = new TlsToolkitStandalone();
+ toolkit.splitKeystore(standaloneConfig);
+ }
+
+ @Test(expected = UnrecoverableKeyException.class)
+ public void testSplitKeystoreWrongKeyPass() throws Exception {
+ tlsToolkitStandaloneCommandLine.parse("-splitKeystore", keystoreFile,
"-S", keystorePass, "-K", wrongPass, "-o", outputFolder.getPath());
+ StandaloneConfig standaloneConfig =
tlsToolkitStandaloneCommandLine.createSplitKeystoreConfig();
+
+ TlsToolkitStandalone toolkit = new TlsToolkitStandalone();
+ toolkit.splitKeystore(standaloneConfig);
+ }
+
+ @Test(expected = IOException.class)
+ public void testSplitKeystoreWrongKeystorePass() throws Exception {
+ tlsToolkitStandaloneCommandLine.parse("-splitKeystore", keystoreFile,
"-S", wrongPass, "-K", keyPass, "-o", outputFolder.getPath());
+ StandaloneConfig standaloneConfig =
tlsToolkitStandaloneCommandLine.createSplitKeystoreConfig();
+
+ TlsToolkitStandalone toolkit = new TlsToolkitStandalone();
+ toolkit.splitKeystore(standaloneConfig);
+ }
+
+ @Rule
+ public ExpectedException expectedEx = ExpectedException.none();
+
+ @Test
+ public void testSplitKeystoreNoKeystore() throws Exception {
+ expectedEx.expect(CommandLineParseException.class);
+ expectedEx.expectMessage("Error parsing command line. (Missing
argument for option: splitKeystore)");
+
+ tlsToolkitStandaloneCommandLine.parse("-splitKeystore", "-S",
keystorePass, "-K", keyPass, "-o", outputFolder.getPath());
+ StandaloneConfig standaloneConfig =
tlsToolkitStandaloneCommandLine.createSplitKeystoreConfig();
+
+ TlsToolkitStandalone toolkit = new TlsToolkitStandalone();
+ toolkit.splitKeystore(standaloneConfig);
+ }
+
+ @Test
+ public void testSplitKeystoreEmptyKeystore() throws Exception {
+ expectedEx.expect(KeyStoreException.class);
+ expectedEx.expectMessage("was empty. No cert/key pairs to output to
file.");
+
+ tlsToolkitStandaloneCommandLine.parse(
+ "-splitKeystore", new
File("src/test/resources/empty-keystore.jks").getPath(), "-S", keystorePass,
"-K", keyPass, "-o", outputFolder.getPath());
+ StandaloneConfig standaloneConfig =
tlsToolkitStandaloneCommandLine.createSplitKeystoreConfig();
+
+ assertTrue(standaloneConfig.isSplitKeystore());
+ assertEquals(keyPass, standaloneConfig.getKeyPassword());
+ assertEquals(keystorePass, standaloneConfig.getKeyStorePassword());
+ TlsToolkitStandalone toolkit = new TlsToolkitStandalone();
+ toolkit.splitKeystore(standaloneConfig);
+ }
+
}
diff --git
a/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/util/TlsHelperTest.java
b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/util/TlsHelperTest.java
index e7efabb..5ed4e91 100644
---
a/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/util/TlsHelperTest.java
+++
b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/util/TlsHelperTest.java
@@ -17,68 +17,80 @@
package org.apache.nifi.toolkit.tls.util;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.security.util.CertificateUtils;
+import org.apache.nifi.toolkit.tls.configuration.TlsConfig;
+import org.bouncycastle.asn1.pkcs.Attribute;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openssl.PEMKeyPair;
+import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.mockito.AdditionalMatchers;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
+import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
+import java.security.KeyStoreException;
import java.security.KeyStoreSpi;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.PublicKey;
+import java.security.Security;
import java.security.SignatureException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
+import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.nifi.security.util.CertificateUtils;
-import org.apache.nifi.toolkit.tls.configuration.TlsConfig;
-import org.bouncycastle.asn1.pkcs.Attribute;
-import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import org.bouncycastle.asn1.x509.Extension;
-import org.bouncycastle.asn1.x509.Extensions;
-import org.bouncycastle.asn1.x509.GeneralName;
-import org.bouncycastle.asn1.x509.GeneralNames;
-import org.bouncycastle.cert.X509CertificateHolder;
-import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.openssl.PEMKeyPair;
-import org.bouncycastle.openssl.PEMParser;
-import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
-import org.bouncycastle.operator.OperatorCreationException;
-import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.AdditionalMatchers;
-import org.mockito.Mock;
-import org.mockito.runners.MockitoJUnitRunner;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class TlsHelperTest {
@@ -98,6 +110,8 @@ public class TlsHelperTest {
private KeyStore keyStore;
+ private String password = "changeit";
+
@Mock
KeyStoreSpi keyStoreSpi;
@@ -107,6 +121,9 @@ public class TlsHelperTest {
@Mock
OutputStreamFactory outputStreamFactory;
+ @Rule
+ public TemporaryFolder tempFolder = new TemporaryFolder();
+
private ByteArrayOutputStream tmpFileOutputStream;
private File file;
@@ -448,4 +465,66 @@ public class TlsHelperTest {
assertEquals("CN=testuser_OU=NiFi_Organisation", escapedClientDn);
}
+ private KeyStore setupKeystore() throws CertificateException,
NoSuchAlgorithmException, IOException, KeyStoreException {
+
+ KeyStore ks = KeyStore.getInstance("JKS");
+ InputStream readStream =
getClass().getClassLoader().getResourceAsStream("keystore.jks");
+ ks.load(readStream, password.toCharArray());
+
+ return ks;
+ }
+
+ @Test
+ public void testOutputToFileTwoCertsAsPem() throws IOException,
CertificateException, NoSuchAlgorithmException, KeyStoreException {
+ Security.addProvider(new
org.bouncycastle.jce.provider.BouncyCastleProvider());
+ File folder = tempFolder.newFolder("splitKeystoreOutputDir");
+
+ KeyStore keyStore = setupKeystore();
+ HashMap<String, Certificate> certs = TlsHelper.extractCerts(keyStore);
+ TlsHelper.outputCertsAsPem(certs, folder,".crt");
+
+ assertEquals(folder.listFiles().length, 2);
+
+ for(File file : folder.listFiles()) {
+ X509Certificate certFromFile = loadCertificate(file);
+ assertTrue(certs.containsValue(certFromFile));
+ X509Certificate originalCert = (X509Certificate)
certs.get(file.getName().split("\\.")[0]);
+ assertTrue(originalCert.equals(certFromFile));
+ assertArrayEquals(originalCert.getSignature(),
certFromFile.getSignature());
+ }
+ }
+
+ // Keystore contains two certificates, but one key. This is to test the
edge case where a certificate does not have a key.
+ @Test
+ public void testOutputToFileOneKeyAsPem() throws IOException,
CertificateException, NoSuchAlgorithmException, KeyStoreException,
UnrecoverableKeyException {
+ File folder = tempFolder.newFolder("splitKeystoreOutputDir");
+ KeyStore keyStore = setupKeystore();
+ HashMap<String, Key> keys = TlsHelper.extractKeys(keyStore,
password.toCharArray());
+ TlsHelper.outputKeysAsPem(keys, folder, ".key");
+
+ for(File file : folder.listFiles()) {
+ BufferedReader br = new BufferedReader(new FileReader(file));
+ PEMParser pemParser = new PEMParser(br);
+ PEMKeyPair key = (PEMKeyPair) pemParser.readObject();
+
assertArrayEquals(keys.get(file.getName().split("\\.")[0]).getEncoded(),
key.getPrivateKeyInfo().getEncoded());
+ }
+ }
+
+ @Test
+ public void testExtractCerts() throws IOException, CertificateException,
NoSuchAlgorithmException, KeyStoreException {
+ KeyStore keyStore = setupKeystore();
+ HashMap<String, Certificate> certs = TlsHelper.extractCerts(keyStore);
+ assertEquals(2, certs.size());
+ certs.forEach((String p, Certificate q) -> assertEquals("X.509",
q.getType()));
+ }
+
+ @Test
+ public void testExtractKeys() throws IOException, CertificateException,
NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {
+ KeyStore keyStore = setupKeystore();
+ HashMap<String, Key> keys = TlsHelper.extractKeys(keyStore,
password.toCharArray());
+ assertEquals(1, keys.size());
+ keys.forEach((String alias, Key key) -> assertEquals("PKCS#8",
key.getFormat()));
+ }
+
+
}
diff --git
a/nifi-toolkit/nifi-toolkit-tls/src/test/resources/empty-keystore.jks
b/nifi-toolkit/nifi-toolkit-tls/src/test/resources/empty-keystore.jks
new file mode 100644
index 0000000..c408465
Binary files /dev/null and
b/nifi-toolkit/nifi-toolkit-tls/src/test/resources/empty-keystore.jks differ
diff --git a/nifi-toolkit/nifi-toolkit-tls/src/test/resources/keystore.jks
b/nifi-toolkit/nifi-toolkit-tls/src/test/resources/keystore.jks
new file mode 100644
index 0000000..1eed4dd
Binary files /dev/null and
b/nifi-toolkit/nifi-toolkit-tls/src/test/resources/keystore.jks differ