From d47fd55a4108a4ca6b8e4179d75572348d0d6a8b Mon Sep 17 00:00:00 2001 From: thenatog Date: Sun, 24 Feb 2019 21:13:49 -0500 Subject: [PATCH 1/5] 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. --- .../src/test/resources/keystore.jks | Bin 0 -> 3088 bytes .../tls/configuration/StandaloneConfig.java | 9 +++ .../tls/standalone/TlsToolkitStandalone.java | 28 ++++++++ .../TlsToolkitStandaloneCommandLine.java | 52 ++++++++++++-- .../nifi/toolkit/tls/util/TlsHelper.java | 63 +++++++++++++++++ .../TlsToolkitStandaloneCommandLineTest.java | 42 +++++++++++ .../nifi/toolkit/tls/util/TlsHelperTest.java | 66 ++++++++++++++++++ .../src/test/resources/keystore.jks | Bin 0 -> 3975 bytes 8 files changed, 255 insertions(+), 5 deletions(-) create mode 100644 nifi-commons/nifi-security-utils/src/test/resources/keystore.jks create mode 100644 nifi-toolkit/nifi-toolkit-tls/src/test/resources/keystore.jks 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 0000000000000000000000000000000000000000..246fe888efbb981188c9dcca109b5d2c52f2b435 GIT binary patch literal 3088 zcmb`|c{tSF9suz9%_a@TzDvU}%JBY1_7`DFP1&+#U$ZAATQ9;`LzV{F%6MfdJ87|v zE2AhhmLf7Oyo_p0MWJw2_jzyc^W6K~`^Wpod7kq<=RDu@$M<}`XY<|WI|u{<-5$XA zvE!7#kH1Qw_qpu_?~N4xO$Y=51D%~@XpW4zpN#`sJCe8iw3r=g#nrL- zc}e<${}VaCYCr4gf{ezT(R=D z-nlpW#qF(FTF*1$2O#WiubmF;Hrxmy6*igNRlfI9l~@uN!qhs~FEEGiMBlUNgZzPX(j)?E&4g~kNMFchr_X$68d zLt}BE%T`bPS@41`N-iwN`9RK`z@zznQIT?}DVA}T$?N6wuhG{w2?-ZIV8Z4AapH~> zM=w+HO5Fp%YPU&g%=71;K{*z_W9CqOV7L|boJ98Vakn zj}6I7pIDmnvrHDGH%#ao7SjJ1`5HddZo{Zee{!RGW`+H+BrM;}xmtv)l}OxJAZh8- zwT`Bb9j|;Yw_=HDi5fgOoK;}HBaAL{SGgCDyYgC&a%im9w+!Rh4OO{I+3JX2BP;2q`9{!Jli?d3(E_2u<*?$NuwN zAx~K%jw`&zdYQkM#CBOGN@Z)7Q<2%226`=Tiu8=+T0z;V zY7d2carE2qJ_kfpxZ~T^YKhz^<)mJ)c!*1P+p8$2xI%eB(zQC(9^*_&h|gtE3?{fC_^JA?rpHl7SEY8Zz?j!0+^9w##-C~jZ9C0k?>zlY?s;}AZqkO zbydnL6y`L;WU);zfw*aVg|DuDb_E^dVtr1h)_rk&z2RQwX?`5pO;@!2(%#Elv zUe?<*C&k;IN?al00~>7(D+{R{ic4NlN$k7d`Kle9?n&9GU&+1B-px6N;$i0b^mLdH z$(AH9DJAw4%+ES<-~96R++imu41fUT@mIn4Vo+wg1TuVZQMj;q`m}DIxpU)D>FgW# zCt@$))iSz4*>BtOaB)yHPFQ#tq@;HhsC0~}gtS`Jb~GUw{o7yR_5m~iY{B6$C~Otv z{uT?tp&;Z(Y6Z9`D2&{({aqpuTrlXLGvG&Rfp4kF|9${JO@A)o_WRi`ApkLd%?d`^ zwVRHvkb+>ylrv{t<84w-ewm=TU#?Km9_vmc&9~O%6%D7U^XiNP=!ZoKGTfS%Z}Nw) zH{x9_j=9q}jQmOY)74O+sC%~#z>AO)@0IaZlNPG}wW`Bg=)g&(tXNyq_!yt+OrN=~ zI($%TXQXiyc7nf@_hT*hgCy0khUrx^=WzesaX3$8g63@cB7^&p{McFHy1vdY==?h9 zr$i9-K5UdfLvmB==xlIx+U1VV&1Bu2@vaB97}@x`Igcs&i$SY;of|i20_+_wEhLNk z+=yMjh0Gcl{PF_zdC}bg6b%M;P1(`c(n3>q$pDAw=cgoWJ+UFRdnX}(x-;8$N7{Br z_{kkw;BRtA_^UgDhJ(-XdZNP~%U{gf7FCwHzJh|Rf_+h9s``swy%q}DLj3H6C zm&5jubG)kdY)^pIY2viya-LOlTTWE{Q#6J~$`{9ISBVxD5%5QXmr|5=0xtV6gXHrVgG0f?qugFkf{fs5ABY4YFOs`kuIZEzZ*rUX}v9X zN><>P7`!CrHo!+=rw-Pd*t`9=BZ6as?F1UlV0w}EQMEuBMHopBcmt14|g&sQmlf^%aznq zUn5%e{qm(@k9XT@{BRcw#{5+cunF?~P=f$r+me1V`5(#sPm|wG|95zQ?aSX?xh;m} z?PMiEdK%D&u3H6GI^rhnY*xAGv<;Iqj>{WtWW@EZletfeN9l*o^*{aEh^`J6S`PPx zb!lA+afuH^Y<;QMQN8n<1{6v(4GYP(NR)PaHRS}EU0EA{9Fv&6>jAFDwB;aj&{_Ji z%^1H5<93JiC5io0Rj&h9D-N^eGlyk6Ijv?(^|B%=o%YPcg*(0Cu1 literal 0 HcmV?d00001 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 d8e86347a94d..4fd6af596072 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 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 List getInstanceDefinitions() { public void setInstanceDefinitions(List 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 ffe4c5d60d7c..324559e177c8 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 @@ -18,18 +18,25 @@ package org.apache.nifi.toolkit.tls.standalone; 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; import org.apache.nifi.security.util.CertificateUtils; import org.apache.nifi.security.util.KeyStoreUtils; @@ -66,6 +73,26 @@ public TlsToolkitStandalone(OutputStreamFactory outputStreamFactory) { this.outputStreamFactory = outputStreamFactory; } + private void splitKeystore(KeyStore keyStore, char[] keyPassphrase, File outputDirectory) throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException { + HashMap certificates = TlsHelper.extractCerts(keyStore); + HashMap keys = TlsHelper.extractKeys(keyStore, keyPassphrase); + TlsHelper.outputCertsAsPem(certificates, outputDirectory, ".crt"); + TlsHelper.outputKeysAsPem(keys, outputDirectory, ".key"); + } + + public void splitKeystore(StandaloneConfig standaloneConfig) throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException { + KeyStore keyStore = KeyStore.getInstance("JKS"); + char[] keyStorePassword; + if(standaloneConfig.getKeyStorePassword() != null) { + keyStorePassword = standaloneConfig.getKeyStorePassword().toCharArray(); + } else { + + } + + keyStore.load(new FileInputStream(standaloneConfig.getKeyStore()), standaloneConfig.getKeyStorePassword().toCharArray()); + splitKeystore(keyStore, standaloneConfig.getKeyPassword().toCharArray(), standaloneConfig.getBaseDir()); + } + 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 +242,7 @@ public void createNifiKeystoresAndTrustStores(StandaloneConfig standaloneConfig) 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 f11ad7b1b364..0ac41f20300f 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 @@ -60,6 +60,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 +87,14 @@ protected static String calculateDefaultOutputDirectory(Path currentPath) { private List 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 +118,8 @@ protected TlsToolkitStandaloneCommandLine(PasswordUtil passwordUtil) { 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("splitKeystore", 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 +129,25 @@ public static void main(String[] args) { } 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()); + + StandaloneConfig conf = tlsToolkitStandaloneCommandLine.createSplitKeystoreConfig(); + + if(conf.isSplitKeystore()) { + 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 @@ protected CommandLine doParse(String... args) throws CommandLineParseException { 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) && commandLine.hasOption(KEY_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 or keyPassword 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 StandaloneConfig createConfig() { 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 20bc2d9ff132..e23c28180ed3 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 @@ -18,21 +18,29 @@ package org.apache.nifi.toolkit.tls.util; 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; import javax.crypto.Cipher; import javax.crypto.Mac; @@ -69,6 +77,7 @@ import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest; import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder; import org.bouncycastle.util.io.pem.PemWriter; +import org.bouncycastle.openssl.jcajce.JcaPEMWriter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -146,6 +155,60 @@ public static String writeKeyStore(KeyStore keyStore, OutputStreamFactory output return password; } + public static HashMap extractCerts(KeyStore keyStore) throws KeyStoreException { + HashMap certs = new HashMap<>(); + Enumeration certAliases = keyStore.aliases(); + while(certAliases.hasMoreElements()) { + String alias = certAliases.nextElement(); + certs.put(alias, keyStore.getCertificate(alias)); + } + return certs; + } + + public static HashMap extractKeys(KeyStore keyStore, char[] privKeyPass) throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException { + HashMap keys = new HashMap<>(); + Enumeration 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 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 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 0fe004aee008..bc7a607932f3 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 @@ -27,7 +27,9 @@ 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.TemporaryFolder; import org.mockito.internal.stubbing.defaultanswers.ForwardsInvocations; import java.io.ByteArrayInputStream; @@ -50,6 +52,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; +import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; @@ -58,11 +61,15 @@ public class TlsToolkitStandaloneCommandLineTest { private SecureRandom secureRandom; private TlsToolkitStandaloneCommandLine tlsToolkitStandaloneCommandLine; + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); + @Before public void setup() { secureRandom = mock(SecureRandom.class); doAnswer(new ForwardsInvocations(new Random())).when(secureRandom).nextBytes(any(byte[].class)); tlsToolkitStandaloneCommandLine = new TlsToolkitStandaloneCommandLine(new PasswordUtil(secureRandom)); + } @Test @@ -445,4 +452,39 @@ private Properties getProperties() throws IOException { properties.load(new ByteArrayInputStream(byteArrayOutputStream.toByteArray())); return properties; } + + @Test + public void testSplitKeystore() throws Exception { + + String keyPass = "changeit"; + String keystorePass = "changeit"; + File folder = tempFolder.newFolder("splitKeystoreOutputDir"); + tlsToolkitStandaloneCommandLine.parse("-splitKeystore", getClass().getClassLoader().getResource("keystore.jks").getFile(), "-S", keystorePass, "-K", keyPass, "-o", folder.getPath()); + StandaloneConfig standaloneConfig = tlsToolkitStandaloneCommandLine.createSplitKeystoreConfig(); + System.out.println(standaloneConfig.getKeyPassword()); + + assertTrue(standaloneConfig.isSplitKeystore()); + assertEquals(keyPass, standaloneConfig.getKeyPassword()); + assertEquals(keystorePass, standaloneConfig.getKeyStorePassword()); + TlsToolkitStandalone toolkit = new TlsToolkitStandalone(); + toolkit.splitKeystore(standaloneConfig); + + assertTrue(folder.listFiles().length > 0); + for(File file : folder.listFiles()) { + assertTrue(file.length() > 0); + } + } + + @Test(expected = CommandLineParseException.class) + public void testSplitKeystoreMissingPasswords() throws Exception { + + String keyPass = "changeit"; + String keystorePass = "changeit"; + File folder = tempFolder.newFolder("splitKeystoreOutputDir"); + tlsToolkitStandaloneCommandLine.parse("-splitKeystore", getClass().getClassLoader().getResource("keystore.jks").getFile()); + StandaloneConfig standaloneConfig = tlsToolkitStandaloneCommandLine.createSplitKeystoreConfig(); + + 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 e7efabb30ac3..0a2b8753b77d 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 @@ -18,6 +18,7 @@ package org.apache.nifi.toolkit.tls.util; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Matchers.eq; @@ -26,16 +27,19 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +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; @@ -45,14 +49,17 @@ import java.security.Provider; import java.security.PublicKey; import java.security.SignatureException; +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; @@ -72,7 +79,9 @@ 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; @@ -107,6 +116,9 @@ public class TlsHelperTest { @Mock OutputStreamFactory outputStreamFactory; + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); + private ByteArrayOutputStream tmpFileOutputStream; private File file; @@ -448,4 +460,58 @@ public void testClientDnFilenameSpecialChars() throws Exception { assertEquals("CN=testuser_OU=NiFi_Organisation", escapedClientDn); } + private KeyStore setupKeystore() throws Exception { + + KeyStore ks = KeyStore.getInstance("JKS"); + InputStream readStream = getClass().getClassLoader().getResourceAsStream("keystore.jks"); + ks.load(readStream, "changeit".toCharArray()); + + return ks; + } + + @Test + public void testOutputCertsAsPem() throws Exception { + File folder = tempFolder.newFolder("splitKeystoreOutputDir"); + KeyStore keyStore = setupKeystore(); + HashMap certs = TlsHelper.extractCerts(keyStore); + TlsHelper.outputCertsAsPem(certs, folder,".crt"); + + for(File file : folder.listFiles()) { + BufferedReader br = new BufferedReader(new FileReader(file)); + PEMParser pemParser = new PEMParser(br); + X509CertificateHolder key = (X509CertificateHolder) pemParser.readObject(); + assertNotNull(key.getSignature()); + } + } + + @Test + public void testOutputKeysAsPem() throws Exception { + File folder = tempFolder.newFolder("splitKeystoreOutputDir"); + KeyStore keyStore = setupKeystore(); + HashMap keys = TlsHelper.extractKeys(keyStore, "changeit".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(); + assertNotNull(key.getPrivateKeyInfo()); + } + } + + @Test + public void testExtractCerts() throws Exception { + KeyStore keyStore = setupKeystore(); + HashMap certs = TlsHelper.extractCerts(keyStore); + assertEquals(2, certs.size()); + certs.forEach((String p, Certificate q) -> assertEquals("X.509", q.getType())); + } + + @Test + public void testExtractKeys() throws Exception { + KeyStore keyStore = setupKeystore(); + HashMap keys = TlsHelper.extractKeys(keyStore, "changeit".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/keystore.jks b/nifi-toolkit/nifi-toolkit-tls/src/test/resources/keystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..1eed4dd9d594fee176061cd61dfb1acbc6bcdafd GIT binary patch literal 3975 zcmeH}XHb*r7RU3ZQIdc(X+{vF7zn~uIlDOvfk2>t z900=V>E`T)cXtYcKmgbFa@he01Ob2%@C}dy$u5Hg-~bNF1po*LJOVzMHKFO$5v&KD zz*!69PZLagMr}&3s|JmBbB$Dd;fsf|I*)oLMbdk;QH`pdHp_z+buO>!utKihM3;{yt=os z(=N}u57PWw2m|&ET{CJYZz^B56G~Y#(#WEVuR>n)CP$pM!y3obO#I>LS>c|_6X{_X z+94zT*={n26*pPv@-9%cb@6=JiCiXUV5vT2vZ#j$F&FU9#6z&I9%CBM%M2T@Fk`~} z=tVC4k)>mwazZA-$XP^3PLKzc7 zsKEr9EL;BWGNB${B?Sr62Z@-crD4WGHsknI&q+6eS&R6psL~Yk+2AXph==!{DQcau zh*Vl{$Cppu8k&xTilqu%MP+Bf-t^ay$(doRm-DQ_@A2fQ3}-rm$k`aY+#ol;rL#9y zBDV#iW<1DJatsM#kGyrrn$kQ2bgVs%n;6i)a_a{E;A<{5gx6S}ia8MYy1x zwuE}-s}{Mau&4xezLnYS0 zO^|+1Y4Y-$+XN{J`1>YUnbVd<7V*vUl@Eh{@xW2N_CpHxJH)*WG!L;Y@RG<|oPW4N z1bX*e|5KB9?|t#?N21T8mq zraM;k@^VUePFqPZRMjS4YVLb>@kY)`Ra*=$sb#54ve177o}oNRQB)-{oF5?1W$5cZ zRckVQ{88s5`MFy6RUNU5sV?F{apQeOr^PBR!b~KMo;%N(U^9!4`k#N~D&T{#Wl1O9 z8OVDr9ukmzSWCO+5Y0^Rdi-1ejG42S1DP&*PZWqp=&USvj_QaXs}9DEP+E@lJR<3p zRn$g@OV=I@lm>?9b4}_n%3&e~O>;6+A53RIwfe;`agOdjZGqYyhc?P!Wsd<%nOD+g zdSxcNx$0kL1pLA3Ja*Lz+1mkw4(NO!^ zaB19xff~&ndWEH;LF{v%v*;QwdNl#xAm_O+hPA$~N$K$D+mvFH@BWvT{jcG<-sEr` zzq@l+{0iv|K1JhMrmI$BhG@NzpY1%Rz4}=J7rFH*^7Pu1hv3OY8Npo1Lia(vJ)1U~ z^vqW-^V4koBNHobjZ@xCY*j`uE~qOu|3F)j$(PW+kPW??ABfIdBs7PxP3vK>_SYl|NqLGolzVD;ZC+z(hbEmpt&Upg6Y$W?7748lj zDVK%tb1hq#4oEWE`%?yKJ7u1c;K-mVT9g)h-w=YJrw`g_b%Gf8J2dVoL1 zBjS{t5~v1&z|MgYFgq{;s#^eq0#GRX_Mj{z8*PtJ2#7(l;iC1TzhVFYg@AzK@)!_> zgq?(=&~OudU440OaK~>h49!OJayUzJ^}65(?&inHtAPrjiUO#hsH9}Yk5SmRK;=K& z|1L=cAola;+XEUF0dPXV2!I_L0RWKejqbhYlJEj;xt7(kR{Q}#oMV=%<<;EOe1FS{le6T zUuI!hy38jVzif?N)o$XB;P}#22EG{V%Q9`|-kCczm2BC0 zajx{$Ns-ad4MaCCv;HC>iL8r`c1h^AJcl+8${VJju3?VwXSPCnsp& zTR6?1rv3WK;3?JdrEe{R4o*hCl|5H^tF}gvmlTy7g83E{gG@)==*X?{wP*X*6%4B+ zPoe?{Wp)ddf+`d8vuOx%m?Gj));5*^G{@kfIr*wr<>9>d^ zRB&RIcF7?jP~^8~akv1uGo062>y2x5EB0C9y}+`W)wwf~{5GuHO1K@s2>@GgI1CC~ z1=)V{dEihG@*}kbd?(0@WZhOv1cC(y{nG-rZ?_}4;M+|R@lyl*wQ08N20*NwtEE;f znp7&UuP1XDiG|Bq?}!mwr=7BE`9xh?h`C?jm<>Pb(|aqC&rYj0aHrqgY*Sav2K{>W zU9*KkoR92MLOWc*;$B0k1us+z%_j6f;f5WIh9bNuhWZSb*VLz58))(gjrxobAykcz zv&BP-!V&J8OZ~V;ae-r(@(xCqgnEIaXyl6zd$4cI&Ha7qH|=Di-@jNvEAhyN`VEN7 z?860X&_XTpUYC#@)*eZ{drDX-JJe9_W@v-C-F>u+(U`mZzUqGOmwXE#+_e2B`9J6`WHWDMZ+I_qoXHbwoVKd%_O@A9U z`JZ4*js5?IEk0W6>MhjfLgiUT$<(mi3AiPg2$LLNm@iRH$wHY5mwCOdOn!;$nye#F zXD6pTslU`uzgn`13_FBz=U+}wYp^OkMG-NHy2^QL zq5Fi@i_SYHj$)VUD>y!Ql1??hSPU|#N*u|uKQU80iJ?{{(2y+VR9(H!Gcuvp!^M1T zd8-4XPI&~4DaqNVVij*?dpuw{A-&0-JurBLA%yR*X$OE zzE{i&qJF>@244OvD!?X){(%Ytgl*&V6NrDy*?$D_caZ-*ZGXen{^uUYQ>9PLrae|X z?z=h6u$(`3pg8DFEnpJ(9wFvtF?7dvqf$qH2g>soZii%KH{RdgU@GhA@y{wG-B@)5 zRh%HXY#DkQqg!{(X~D{<0oswDXBf4@r?P5SpXYRUU6Cgj`8{BbZ@gCc9IM70(-7Ou zzVMySD1V`|?q@YN0#{m=jnqz;&R2gz^gTz1)I_Zkugm9f&+=9qwxY+r$0yW(`@VsjCI^G#>m*Nv!^ Date: Wed, 27 Feb 2019 13:46:08 -0500 Subject: [PATCH 2/5] NIFI-6026 - Removed some code that is not necessary after doing some argument checking in the command line parsing. --- .../nifi/toolkit/tls/standalone/TlsToolkitStandalone.java | 7 ------- 1 file changed, 7 deletions(-) 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 324559e177c8..0309a4bd09ed 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 @@ -82,13 +82,6 @@ private void splitKeystore(KeyStore keyStore, char[] keyPassphrase, File outputD public void splitKeystore(StandaloneConfig standaloneConfig) throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException { KeyStore keyStore = KeyStore.getInstance("JKS"); - char[] keyStorePassword; - if(standaloneConfig.getKeyStorePassword() != null) { - keyStorePassword = standaloneConfig.getKeyStorePassword().toCharArray(); - } else { - - } - keyStore.load(new FileInputStream(standaloneConfig.getKeyStore()), standaloneConfig.getKeyStorePassword().toCharArray()); splitKeystore(keyStore, standaloneConfig.getKeyPassword().toCharArray(), standaloneConfig.getBaseDir()); } From f5a2b6715f8d9f8f1f8593f9123f4895445cae96 Mon Sep 17 00:00:00 2001 From: thenatog Date: Tue, 12 Mar 2019 12:11:06 -0400 Subject: [PATCH 3/5] NIFI-6026 - Made some small changes to only require keystore password if keystore and key passwords are the same. Added some more tests. --- .../tls/standalone/TlsToolkitStandalone.java | 6 ++- .../TlsToolkitStandaloneCommandLine.java | 9 ++-- .../TlsToolkitStandaloneCommandLineTest.java | 41 ++++++++++++++++++- 3 files changed, 48 insertions(+), 8 deletions(-) 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 0309a4bd09ed..1852764f2017 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 @@ -83,7 +83,11 @@ private void splitKeystore(KeyStore keyStore, char[] keyPassphrase, File outputD 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()); - splitKeystore(keyStore, standaloneConfig.getKeyPassword().toCharArray(), standaloneConfig.getBaseDir()); + if(standaloneConfig.getKeyPassword() == null || standaloneConfig.getKeyPassword().isEmpty()) { + splitKeystore(keyStore, standaloneConfig.getKeyStorePassword().toCharArray(), standaloneConfig.getBaseDir()); + } else { + splitKeystore(keyStore, standaloneConfig.getKeyPassword().toCharArray(), standaloneConfig.getBaseDir()); + } } public void createNifiKeystoresAndTrustStores(StandaloneConfig standaloneConfig) throws GeneralSecurityException, IOException { 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 0ac41f20300f..3c9484677a59 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 @@ -130,9 +130,8 @@ public static void main(String[] args) { System.exit(e.getExitCode().ordinal()); } - StandaloneConfig conf = tlsToolkitStandaloneCommandLine.createSplitKeystoreConfig(); - - if(conf.isSplitKeystore()) { + if(tlsToolkitStandaloneCommandLine.splitKeystore) { + StandaloneConfig conf = tlsToolkitStandaloneCommandLine.createSplitKeystoreConfig(); try { new TlsToolkitStandalone().splitKeystore(conf); } catch (Exception e) { @@ -189,13 +188,13 @@ protected CommandLine doParse(String... args) throws CommandLineParseException { overwrite = commandLine.hasOption(OVERWRITE_ARG); if(commandLine.hasOption(SPLIT_KEYSTORE_ARG)) { - if(commandLine.hasOption(KEY_STORE_PASSWORD_ARG) && commandLine.hasOption(KEY_PASSWORD_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 or keyPassword supplied.", ExitCode.ERROR_INCORRECT_NUMBER_OF_PASSWORDS); + printUsageAndThrow("-splitKeystore specified but no keyStorePassword supplied.", ExitCode.ERROR_INCORRECT_NUMBER_OF_PASSWORDS); } } 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 bc7a607932f3..d787769ee5fe 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 @@ -39,6 +39,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.security.SecureRandom; +import java.security.UnrecoverableKeyException; import java.util.Collections; import java.util.List; import java.util.Map; @@ -478,10 +479,46 @@ public void testSplitKeystore() throws Exception { @Test(expected = CommandLineParseException.class) public void testSplitKeystoreMissingPasswords() throws Exception { - String keyPass = "changeit"; + File folder = tempFolder.newFolder("splitKeystoreOutputDir"); + tlsToolkitStandaloneCommandLine.parse("-splitKeystore", getClass().getClassLoader().getResource("keystore.jks").getFile(), "-o", folder.getPath()); + StandaloneConfig standaloneConfig = tlsToolkitStandaloneCommandLine.createSplitKeystoreConfig(); + + TlsToolkitStandalone toolkit = new TlsToolkitStandalone(); + toolkit.splitKeystore(standaloneConfig); + } + + @Test + public void testSplitKeystoreWithKeystoreAndKeyPasswordTheSame() throws Exception { + + String keystorePass = "changeit"; + File folder = tempFolder.newFolder("splitKeystoreOutputDir"); + tlsToolkitStandaloneCommandLine.parse("-splitKeystore", getClass().getClassLoader().getResource("keystore.jks").getFile(), "-S", keystorePass, "-o", folder.getPath()); + StandaloneConfig standaloneConfig = tlsToolkitStandaloneCommandLine.createSplitKeystoreConfig(); + + TlsToolkitStandalone toolkit = new TlsToolkitStandalone(); + toolkit.splitKeystore(standaloneConfig); + } + + @Test(expected = UnrecoverableKeyException.class) + public void testSplitKeystoreWrongKeyPass() throws Exception { + + String keyPass = "wrongpass"; String keystorePass = "changeit"; File folder = tempFolder.newFolder("splitKeystoreOutputDir"); - tlsToolkitStandaloneCommandLine.parse("-splitKeystore", getClass().getClassLoader().getResource("keystore.jks").getFile()); + tlsToolkitStandaloneCommandLine.parse("-splitKeystore", getClass().getClassLoader().getResource("keystore.jks").getFile(), "-S", keystorePass, "-K", keyPass, "-o", folder.getPath()); + StandaloneConfig standaloneConfig = tlsToolkitStandaloneCommandLine.createSplitKeystoreConfig(); + + TlsToolkitStandalone toolkit = new TlsToolkitStandalone(); + toolkit.splitKeystore(standaloneConfig); + } + + @Test(expected = IOException.class) + public void testSplitKeystoreWrongKeystorePass() throws Exception { + + String keyPass = "changeit"; + String keystorePass = "wrongpass"; + File folder = tempFolder.newFolder("splitKeystoreOutputDir"); + tlsToolkitStandaloneCommandLine.parse("-splitKeystore", getClass().getClassLoader().getResource("keystore.jks").getFile(), "-S", keystorePass, "-K", keyPass, "-o", folder.getPath()); StandaloneConfig standaloneConfig = tlsToolkitStandaloneCommandLine.createSplitKeystoreConfig(); TlsToolkitStandalone toolkit = new TlsToolkitStandalone(); From 33d32f19e32c1b177749ae709ac7e8ed2c89e872 Mon Sep 17 00:00:00 2001 From: thenatog Date: Thu, 18 Apr 2019 11:30:37 -0400 Subject: [PATCH 4/5] NIFI-6026 - Added some more unit tests as per Andy's request. Also added a check for empty keystores. Made tests a bit cleaner. --- .../tls/standalone/TlsToolkitStandalone.java | 56 +++++---- .../TlsToolkitStandaloneCommandLine.java | 27 +++-- .../nifi/toolkit/tls/util/TlsHelper.java | 59 ++++----- .../TlsToolkitStandaloneCommandLineTest.java | 81 ++++++++----- .../nifi/toolkit/tls/util/TlsHelperTest.java | 113 ++++++++++-------- 5 files changed, 192 insertions(+), 144 deletions(-) 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 1852764f2017..7407e5d53586 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,6 +17,25 @@ package org.apache.nifi.toolkit.tls.standalone; +import org.apache.nifi.security.util.CertificateUtils; +import org.apache.nifi.security.util.KeyStoreUtils; +import org.apache.nifi.security.util.KeystoreType; +import org.apache.nifi.toolkit.tls.configuration.InstanceDefinition; +import org.apache.nifi.toolkit.tls.configuration.StandaloneConfig; +import org.apache.nifi.toolkit.tls.configuration.TlsClientConfig; +import org.apache.nifi.toolkit.tls.manager.TlsCertificateAuthorityManager; +import org.apache.nifi.toolkit.tls.manager.TlsClientManager; +import org.apache.nifi.toolkit.tls.manager.writer.NifiPropertiesTlsClientConfigWriter; +import org.apache.nifi.toolkit.tls.properties.NiFiPropertiesWriterFactory; +import org.apache.nifi.toolkit.tls.util.OutputStreamFactory; +import org.apache.nifi.toolkit.tls.util.TlsHelper; +import org.apache.nifi.util.StringUtils; +import org.bouncycastle.asn1.x509.Extensions; +import org.bouncycastle.openssl.jcajce.JcaMiscPEMGenerator; +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; @@ -38,24 +57,6 @@ import java.util.ArrayList; import java.util.HashMap; 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; -import org.apache.nifi.toolkit.tls.configuration.InstanceDefinition; -import org.apache.nifi.toolkit.tls.configuration.StandaloneConfig; -import org.apache.nifi.toolkit.tls.configuration.TlsClientConfig; -import org.apache.nifi.toolkit.tls.manager.TlsCertificateAuthorityManager; -import org.apache.nifi.toolkit.tls.manager.TlsClientManager; -import org.apache.nifi.toolkit.tls.manager.writer.NifiPropertiesTlsClientConfigWriter; -import org.apache.nifi.toolkit.tls.properties.NiFiPropertiesWriterFactory; -import org.apache.nifi.toolkit.tls.util.OutputStreamFactory; -import org.apache.nifi.toolkit.tls.util.TlsHelper; -import org.apache.nifi.util.StringUtils; -import org.bouncycastle.asn1.x509.Extensions; -import org.bouncycastle.openssl.jcajce.JcaMiscPEMGenerator; -import org.bouncycastle.util.io.pem.PemWriter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class TlsToolkitStandalone { public static final String NIFI_KEY = "nifi-key"; @@ -73,16 +74,14 @@ public TlsToolkitStandalone(OutputStreamFactory outputStreamFactory) { this.outputStreamFactory = outputStreamFactory; } - private void splitKeystore(KeyStore keyStore, char[] keyPassphrase, File outputDirectory) throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException { - HashMap certificates = TlsHelper.extractCerts(keyStore); - HashMap keys = TlsHelper.extractKeys(keyStore, keyPassphrase); - TlsHelper.outputCertsAsPem(certificates, outputDirectory, ".crt"); - TlsHelper.outputKeysAsPem(keys, outputDirectory, ".key"); - } - 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 { @@ -90,6 +89,13 @@ public void splitKeystore(StandaloneConfig standaloneConfig) throws IOException, } } + private void splitKeystore(KeyStore keyStore, char[] keyPassphrase, File outputDirectory) throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException { + HashMap certificates = TlsHelper.extractCerts(keyStore); + HashMap 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(); 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 3c9484677a59..f4c4d8b34e36 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.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 */ @@ -118,7 +119,7 @@ protected TlsToolkitStandaloneCommandLine(PasswordUtil passwordUtil) { 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("splitKeystore", 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."); + 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."); } 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 e23c28180ed3..3d6d5c596b15 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,34 +17,6 @@ package org.apache.nifi.toolkit.tls.util; -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; -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; @@ -71,16 +43,45 @@ 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; import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest; import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder; import org.bouncycastle.util.io.pem.PemWriter; -import org.bouncycastle.openssl.jcajce.JcaPEMWriter; 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; 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 d787769ee5fe..1cf268ccb5f8 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 @@ -29,6 +29,7 @@ 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; @@ -38,6 +39,7 @@ 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; @@ -52,8 +54,8 @@ 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.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; @@ -62,15 +64,24 @@ 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 @@ -456,11 +467,7 @@ private Properties getProperties() throws IOException { @Test public void testSplitKeystore() throws Exception { - - String keyPass = "changeit"; - String keystorePass = "changeit"; - File folder = tempFolder.newFolder("splitKeystoreOutputDir"); - tlsToolkitStandaloneCommandLine.parse("-splitKeystore", getClass().getClassLoader().getResource("keystore.jks").getFile(), "-S", keystorePass, "-K", keyPass, "-o", folder.getPath()); + tlsToolkitStandaloneCommandLine.parse("-splitKeystore", keystoreFile, "-S", keystorePass, "-K", keyPass, "-o", outputFolder.getPath()); StandaloneConfig standaloneConfig = tlsToolkitStandaloneCommandLine.createSplitKeystoreConfig(); System.out.println(standaloneConfig.getKeyPassword()); @@ -470,17 +477,16 @@ public void testSplitKeystore() throws Exception { TlsToolkitStandalone toolkit = new TlsToolkitStandalone(); toolkit.splitKeystore(standaloneConfig); - assertTrue(folder.listFiles().length > 0); - for(File file : folder.listFiles()) { + assertTrue(outputFolder.listFiles().length == 3); + + for(File file : outputFolder.listFiles()) { assertTrue(file.length() > 0); } } @Test(expected = CommandLineParseException.class) public void testSplitKeystoreMissingPasswords() throws Exception { - - File folder = tempFolder.newFolder("splitKeystoreOutputDir"); - tlsToolkitStandaloneCommandLine.parse("-splitKeystore", getClass().getClassLoader().getResource("keystore.jks").getFile(), "-o", folder.getPath()); + tlsToolkitStandaloneCommandLine.parse("-splitKeystore", keystoreFile, "-o", outputFolder.getPath()); StandaloneConfig standaloneConfig = tlsToolkitStandaloneCommandLine.createSplitKeystoreConfig(); TlsToolkitStandalone toolkit = new TlsToolkitStandalone(); @@ -488,11 +494,8 @@ public void testSplitKeystoreMissingPasswords() throws Exception { } @Test - public void testSplitKeystoreWithKeystoreAndKeyPasswordTheSame() throws Exception { - - String keystorePass = "changeit"; - File folder = tempFolder.newFolder("splitKeystoreOutputDir"); - tlsToolkitStandaloneCommandLine.parse("-splitKeystore", getClass().getClassLoader().getResource("keystore.jks").getFile(), "-S", keystorePass, "-o", folder.getPath()); + public void testSplitKeystoreWithSameKeystoreAndKeyPassword() throws Exception { + tlsToolkitStandaloneCommandLine.parse("-splitKeystore", keystoreFile, "-S", keystorePass, "-o", outputFolder.getPath()); StandaloneConfig standaloneConfig = tlsToolkitStandaloneCommandLine.createSplitKeystoreConfig(); TlsToolkitStandalone toolkit = new TlsToolkitStandalone(); @@ -501,11 +504,7 @@ public void testSplitKeystoreWithKeystoreAndKeyPasswordTheSame() throws Exceptio @Test(expected = UnrecoverableKeyException.class) public void testSplitKeystoreWrongKeyPass() throws Exception { - - String keyPass = "wrongpass"; - String keystorePass = "changeit"; - File folder = tempFolder.newFolder("splitKeystoreOutputDir"); - tlsToolkitStandaloneCommandLine.parse("-splitKeystore", getClass().getClassLoader().getResource("keystore.jks").getFile(), "-S", keystorePass, "-K", keyPass, "-o", folder.getPath()); + tlsToolkitStandaloneCommandLine.parse("-splitKeystore", keystoreFile, "-S", keystorePass, "-K", wrongPass, "-o", outputFolder.getPath()); StandaloneConfig standaloneConfig = tlsToolkitStandaloneCommandLine.createSplitKeystoreConfig(); TlsToolkitStandalone toolkit = new TlsToolkitStandalone(); @@ -514,14 +513,42 @@ public void testSplitKeystoreWrongKeyPass() throws Exception { @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); + } - String keyPass = "changeit"; - String keystorePass = "wrongpass"; - File folder = tempFolder.newFolder("splitKeystoreOutputDir"); - tlsToolkitStandaloneCommandLine.parse("-splitKeystore", getClass().getClassLoader().getResource("keystore.jks").getFile(), "-S", keystorePass, "-K", keyPass, "-o", folder.getPath()); + @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", getClass().getClassLoader().getResource("empty-keystore.jks").getFile(), "-S", keystorePass, "-K", keyPass, "-o", outputFolder.getPath()); StandaloneConfig standaloneConfig = tlsToolkitStandaloneCommandLine.createSplitKeystoreConfig(); + System.out.println(standaloneConfig.getKeyPassword()); + 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 0a2b8753b77d..5ed4e919d970 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,15 +17,34 @@ package org.apache.nifi.toolkit.tls.util; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -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; @@ -43,12 +62,15 @@ 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; @@ -60,34 +82,15 @@ 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.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 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 { @@ -107,6 +110,8 @@ public class TlsHelperTest { private KeyStore keyStore; + private String password = "changeit"; + @Mock KeyStoreSpi keyStoreSpi; @@ -460,47 +465,53 @@ public void testClientDnFilenameSpecialChars() throws Exception { assertEquals("CN=testuser_OU=NiFi_Organisation", escapedClientDn); } - private KeyStore setupKeystore() throws Exception { + private KeyStore setupKeystore() throws CertificateException, NoSuchAlgorithmException, IOException, KeyStoreException { KeyStore ks = KeyStore.getInstance("JKS"); InputStream readStream = getClass().getClassLoader().getResourceAsStream("keystore.jks"); - ks.load(readStream, "changeit".toCharArray()); + ks.load(readStream, password.toCharArray()); return ks; } @Test - public void testOutputCertsAsPem() throws Exception { + 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 certs = TlsHelper.extractCerts(keyStore); TlsHelper.outputCertsAsPem(certs, folder,".crt"); + assertEquals(folder.listFiles().length, 2); + for(File file : folder.listFiles()) { - BufferedReader br = new BufferedReader(new FileReader(file)); - PEMParser pemParser = new PEMParser(br); - X509CertificateHolder key = (X509CertificateHolder) pemParser.readObject(); - assertNotNull(key.getSignature()); + 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 testOutputKeysAsPem() throws Exception { + public void testOutputToFileOneKeyAsPem() throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException { File folder = tempFolder.newFolder("splitKeystoreOutputDir"); KeyStore keyStore = setupKeystore(); - HashMap keys = TlsHelper.extractKeys(keyStore, "changeit".toCharArray()); + HashMap 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(); - assertNotNull(key.getPrivateKeyInfo()); + assertArrayEquals(keys.get(file.getName().split("\\.")[0]).getEncoded(), key.getPrivateKeyInfo().getEncoded()); } } @Test - public void testExtractCerts() throws Exception { + public void testExtractCerts() throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException { KeyStore keyStore = setupKeystore(); HashMap certs = TlsHelper.extractCerts(keyStore); assertEquals(2, certs.size()); @@ -508,10 +519,12 @@ public void testExtractCerts() throws Exception { } @Test - public void testExtractKeys() throws Exception { + public void testExtractKeys() throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException { KeyStore keyStore = setupKeystore(); - HashMap keys = TlsHelper.extractKeys(keyStore, "changeit".toCharArray()); + HashMap keys = TlsHelper.extractKeys(keyStore, password.toCharArray()); assertEquals(1, keys.size()); keys.forEach((String alias, Key key) -> assertEquals("PKCS#8", key.getFormat())); } + + } From 22c81d06aebadd8836737bd2c60ef843ef488000 Mon Sep 17 00:00:00 2001 From: thenatog Date: Thu, 18 Apr 2019 12:17:16 -0400 Subject: [PATCH 5/5] NIFI-6026 - Added empty keystore used by unit tests. --- .../src/test/resources/empty-keystore.jks | Bin 0 -> 32 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 nifi-toolkit/nifi-toolkit-tls/src/test/resources/empty-keystore.jks 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 0000000000000000000000000000000000000000..c408465500cb0af9cfd1f7371422ef8899ae6725 GIT binary patch literal 32 ncmezO_TO6u1_mY|W_Xm5=la|E{*s%M?mC{^wn--0;QAy0^AQhj literal 0 HcmV?d00001