You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by bb...@apache.org on 2016/08/15 14:22:37 UTC

[1/2] nifi git commit: NIFI-2526 - DN order, multiple standalone runs, client certificates - Logic for sorting DN, reversing X500Names before using them to generate certificate - Logging reordered dn - Accounting for limited crypto pkcs12, allowing passw

Repository: nifi
Updated Branches:
  refs/heads/master fd0dd51ff -> fa5da543e


http://git-wip-us.apache.org/repos/asf/nifi/blob/fa5da543/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneTest.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneTest.java b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneTest.java
index 6746c40..af81eae 100644
--- a/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneTest.java
+++ b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneTest.java
@@ -18,27 +18,36 @@
 package org.apache.nifi.toolkit.tls.standalone;
 
 import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.nifi.security.util.CertificateUtils;
 import org.apache.nifi.toolkit.tls.commandLine.BaseCommandLine;
 import org.apache.nifi.toolkit.tls.commandLine.ExitCode;
 import org.apache.nifi.toolkit.tls.configuration.TlsConfig;
+import org.apache.nifi.toolkit.tls.manager.BaseTlsManager;
+import org.apache.nifi.toolkit.tls.service.TlsCertificateAuthorityTest;
 import org.apache.nifi.toolkit.tls.util.TlsHelperTest;
 import org.apache.nifi.util.NiFiProperties;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.security.KeyPair;
 import java.security.KeyStore;
 import java.security.NoSuchAlgorithmException;
 import java.security.Permission;
+import java.security.PrivateKey;
+import java.security.PublicKey;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 import java.security.spec.InvalidKeySpecException;
+import java.util.List;
 import java.util.Properties;
 import java.util.UUID;
 
@@ -95,18 +104,18 @@ public class TlsToolkitStandaloneTest {
 
     @Test
     public void testBadParse() {
-        runAndAssertExitCode(ExitCode.ERROR_PARSING_COMMAND_LINE.ordinal(), "--unknownArgument");
+        runAndAssertExitCode(ExitCode.ERROR_PARSING_COMMAND_LINE, "--unknownArgument");
     }
 
     @Test
     public void testHelp() {
-        runAndAssertExitCode(ExitCode.HELP.ordinal(), "-h");
-        runAndAssertExitCode(ExitCode.HELP.ordinal(), "--help");
+        runAndAssertExitCode(ExitCode.HELP, "-h");
+        runAndAssertExitCode(ExitCode.HELP, "--help");
     }
 
     @Test
     public void testDirOutput() throws Exception {
-        runAndAssertExitCode(0, "-o", tempDir.getAbsolutePath());
+        runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-n", TlsConfig.DEFAULT_HOSTNAME);
         X509Certificate x509Certificate = checkLoadCertPrivateKey(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM);
 
         Properties nifiProperties = checkHostDirAndReturnNifiProperties(TlsConfig.DEFAULT_HOSTNAME, x509Certificate);
@@ -116,7 +125,7 @@ public class TlsToolkitStandaloneTest {
 
     @Test
     public void testDifferentArg() throws Exception {
-        runAndAssertExitCode(0, "-o", tempDir.getAbsolutePath(), "-g");
+        runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-g", "-n", TlsConfig.DEFAULT_HOSTNAME);
         X509Certificate x509Certificate = checkLoadCertPrivateKey(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM);
 
         Properties nifiProperties = checkHostDirAndReturnNifiProperties(TlsConfig.DEFAULT_HOSTNAME, x509Certificate);
@@ -126,7 +135,7 @@ public class TlsToolkitStandaloneTest {
 
     @Test
     public void testFileArg() throws Exception {
-        runAndAssertExitCode(0, "-o", tempDir.getAbsolutePath(), "-f", TEST_NIFI_PROPERTIES);
+        runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-f", TEST_NIFI_PROPERTIES, "-n", TlsConfig.DEFAULT_HOSTNAME);
         X509Certificate x509Certificate = checkLoadCertPrivateKey(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM);
 
         Properties nifiProperties = checkHostDirAndReturnNifiProperties(TlsConfig.DEFAULT_HOSTNAME, x509Certificate);
@@ -134,23 +143,36 @@ public class TlsToolkitStandaloneTest {
     }
 
     @Test
-    public void testHostnamesArgument() throws Exception {
+    public void testHostnamesArgumentOverwrite() throws Exception {
         String nifi1 = "nifi1";
         String nifi2 = "nifi2";
         String nifi3 = "nifi3";
 
-        runAndAssertExitCode(0, "-o", tempDir.getAbsolutePath(), "-n", nifi1 + "," + nifi2 + "," + nifi3);
+        runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-n", nifi1 + "," + nifi2);
         X509Certificate x509Certificate = checkLoadCertPrivateKey(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM);
+        runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-n", nifi3);
 
         checkHostDirAndReturnNifiProperties(nifi1, x509Certificate);
         checkHostDirAndReturnNifiProperties(nifi2, x509Certificate);
         checkHostDirAndReturnNifiProperties(nifi3, x509Certificate);
+        runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-O", "-n", nifi3);
+        checkHostDirAndReturnNifiProperties(nifi3, x509Certificate);
+    }
+
+    @Test
+    public void testHostnamesArgumentNoOverwrite() throws Exception {
+        String nifi = "nifi";
+
+        runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-n", nifi);
+        X509Certificate x509Certificate = checkLoadCertPrivateKey(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM);
+        checkHostDirAndReturnNifiProperties(nifi, x509Certificate);
+        runAndAssertExitCode(ExitCode.ERROR_GENERATING_CONFIG, "-o", tempDir.getAbsolutePath(), "-n", nifi);
     }
 
     @Test
     public void testKeyPasswordArg() throws Exception {
         String testKey = "testKey";
-        runAndAssertExitCode(0, "-o", tempDir.getAbsolutePath(), "-K", testKey);
+        runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-K", testKey, "-n", TlsConfig.DEFAULT_HOSTNAME);
         X509Certificate x509Certificate = checkLoadCertPrivateKey(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM);
 
         Properties nifiProperties = checkHostDirAndReturnNifiProperties(TlsConfig.DEFAULT_HOSTNAME, x509Certificate);
@@ -160,7 +182,7 @@ public class TlsToolkitStandaloneTest {
     @Test
     public void testKeyStorePasswordArg() throws Exception {
         String testKeyStore = "testKeyStore";
-        runAndAssertExitCode(0, "-o", tempDir.getAbsolutePath(), "-S", testKeyStore);
+        runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-S", testKeyStore, "-n", TlsConfig.DEFAULT_HOSTNAME);
         X509Certificate x509Certificate = checkLoadCertPrivateKey(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM);
 
         Properties nifiProperties = checkHostDirAndReturnNifiProperties(TlsConfig.DEFAULT_HOSTNAME, x509Certificate);
@@ -170,13 +192,38 @@ public class TlsToolkitStandaloneTest {
     @Test
     public void testTrustStorePasswordArg() throws Exception {
         String testTrustStore = "testTrustStore";
-        runAndAssertExitCode(0, "-o", tempDir.getAbsolutePath(), "-P", testTrustStore);
+        runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-P", testTrustStore, "-n", TlsConfig.DEFAULT_HOSTNAME);
         X509Certificate x509Certificate = checkLoadCertPrivateKey(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM);
 
         Properties nifiProperties = checkHostDirAndReturnNifiProperties(TlsConfig.DEFAULT_HOSTNAME, x509Certificate);
         assertEquals(testTrustStore, nifiProperties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD));
     }
 
+    @Test
+    public void testClientDnsArg() throws Exception {
+        String clientDn = "OU=NIFI,CN=testuser";
+        String clientDn2 = "OU=NIFI,CN=testuser2";
+        runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-C", clientDn, "-C", clientDn2, "-B", "pass1", "-P", "pass2");
+        X509Certificate x509Certificate = checkLoadCertPrivateKey(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM);
+
+        checkClientCert(clientDn, x509Certificate);
+        checkClientCert(clientDn2, x509Certificate);
+
+        runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-O", "-C", clientDn2, "-B", "pass3");
+        checkClientCert(clientDn2, x509Certificate);
+    }
+
+    @Test
+    public void testClientDnsArgNoOverwrite() throws Exception {
+        String clientDn = "OU=NIFI,CN=testuser";
+        runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-C", clientDn, "-B", "passwor");
+        X509Certificate x509Certificate = checkLoadCertPrivateKey(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM);
+
+        checkClientCert(clientDn, x509Certificate);
+
+        runAndAssertExitCode(ExitCode.ERROR_GENERATING_CONFIG, "-o", tempDir.getAbsolutePath(), "-C", clientDn);
+    }
+
     private X509Certificate checkLoadCertPrivateKey(String algorithm) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, CertificateException {
         KeyPair keyPair = TlsHelperTest.loadKeyPair(new File(tempDir, TlsToolkitStandalone.NIFI_KEY + ".key"));
 
@@ -227,15 +274,40 @@ public class TlsToolkitStandaloneTest {
         assertEquals(rootCert, certificateChain[1]);
         certificateChain[1].verify(rootCert.getPublicKey());
         certificateChain[0].verify(rootCert.getPublicKey());
+        TlsCertificateAuthorityTest.assertPrivateAndPublicKeyMatch(privateKeyEntry.getPrivateKey(), certificateChain[0].getPublicKey());
         return nifiProperties;
     }
 
-    private void runAndAssertExitCode(int exitCode, String... args) {
+    private void checkClientCert(String clientDn, X509Certificate rootCert) throws Exception {
+        String clientDnFile = TlsToolkitStandalone.getClientDnFile(CertificateUtils.reorderDn(clientDn));
+        String password;
+        try (FileReader fileReader = new FileReader(new File(tempDir, clientDnFile + ".password"))) {
+            List<String> lines = IOUtils.readLines(fileReader);
+            assertEquals(1, lines.size());
+            password = lines.get(0);
+        }
+
+        KeyStore keyStore = KeyStore.getInstance(BaseTlsManager.PKCS_12, BouncyCastleProvider.PROVIDER_NAME);
+        try (FileInputStream fileInputStream = new FileInputStream(new File(tempDir, clientDnFile + ".p12"))) {
+            keyStore.load(fileInputStream, password.toCharArray());
+        }
+        PrivateKey privateKey = (PrivateKey) keyStore.getKey(TlsToolkitStandalone.NIFI_KEY, new char[0]);
+        Certificate[] certificateChain = keyStore.getCertificateChain(TlsToolkitStandalone.NIFI_KEY);
+        assertEquals(2, certificateChain.length);
+        assertEquals(rootCert, certificateChain[1]);
+        certificateChain[1].verify(rootCert.getPublicKey());
+        certificateChain[0].verify(rootCert.getPublicKey());
+        PublicKey publicKey = certificateChain[0].getPublicKey();
+        TlsCertificateAuthorityTest.assertPrivateAndPublicKeyMatch(privateKey, publicKey);
+
+    }
+
+    private void runAndAssertExitCode(ExitCode exitCode, String... args) {
         try {
             TlsToolkitStandaloneCommandLine.main(args);
             fail("Expecting exit code: " + exitCode);
         } catch (ExitException e) {
-            assertEquals(exitCode, e.getExitCode());
+            assertEquals(exitCode, ExitCode.values()[e.getExitCode()]);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/fa5da543/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/util/TlsHelperTest.java
----------------------------------------------------------------------
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 a900a4f..66ec890 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
@@ -25,23 +25,32 @@ import org.bouncycastle.openssl.PEMKeyPair;
 import org.bouncycastle.openssl.PEMParser;
 import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
 import org.bouncycastle.operator.OperatorCreationException;
+import org.junit.After;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.AdditionalMatchers;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileReader;
 import java.io.IOException;
 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.KeyPair;
 import java.security.KeyPairGenerator;
 import java.security.KeyStore;
+import java.security.KeyStoreSpi;
 import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchProviderException;
-import java.security.SecureRandom;
+import java.security.Provider;
 import java.security.Security;
 import java.security.SignatureException;
 import java.security.cert.CertificateException;
@@ -51,9 +60,17 @@ import java.util.concurrent.TimeUnit;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
+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 {
+    private static final boolean originalUnlimitedCrypto = TlsHelper.isUnlimitedStrengthCryptographyEnabled();
+
     private int days;
 
     private int keySize;
@@ -62,11 +79,32 @@ public class TlsHelperTest {
 
     private String signingAlgorithm;
 
-    private String keyStoreType;
+    private KeyPairGenerator keyPairGenerator;
 
-    private SecureRandom secureRandom;
+    private KeyStore keyStore;
 
-    private KeyPairGenerator keyPairGenerator;
+    @Mock
+    KeyStoreSpi keyStoreSpi;
+
+    @Mock
+    Provider keyStoreProvider;
+
+    @Mock
+    OutputStreamFactory outputStreamFactory;
+
+    private ByteArrayOutputStream tmpFileOutputStream;
+
+    private File file;
+
+    private static void setUnlimitedCrypto(boolean value) {
+        try {
+            Field isUnlimitedStrengthCryptographyEnabled = TlsHelper.class.getDeclaredField("isUnlimitedStrengthCryptographyEnabled");
+            isUnlimitedStrengthCryptographyEnabled.setAccessible(true);
+            isUnlimitedStrengthCryptographyEnabled.set(null, value);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
 
     public static KeyPair loadKeyPair(Reader reader) throws IOException {
         try (PEMParser pemParser = new PEMParser(reader)) {
@@ -98,15 +136,25 @@ public class TlsHelperTest {
     }
 
     @Before
-    public void setup() throws NoSuchAlgorithmException {
+    public void setup() throws Exception {
         days = 360;
         keySize = 2048;
         keyPairAlgorithm = "RSA";
         signingAlgorithm = "SHA1WITHRSA";
-        keyStoreType = KeyStore.getDefaultType();
-        secureRandom = mock(SecureRandom.class);
         keyPairGenerator = KeyPairGenerator.getInstance(keyPairAlgorithm);
         keyPairGenerator.initialize(keySize);
+        Constructor<KeyStore> keyStoreConstructor = KeyStore.class.getDeclaredConstructor(KeyStoreSpi.class, Provider.class, String.class);
+        keyStoreConstructor.setAccessible(true);
+        keyStore = keyStoreConstructor.newInstance(keyStoreSpi, keyStoreProvider, "faketype");
+        keyStore.load(null, null);
+        file = File.createTempFile("keystore", "file");
+        when(outputStreamFactory.create(file)).thenReturn(tmpFileOutputStream);
+    }
+
+    @After
+    public void tearDown() {
+        setUnlimitedCrypto(originalUnlimitedCrypto);
+        file.delete();
     }
 
     private Date inFuture(int days) {
@@ -127,7 +175,7 @@ public class TlsHelperTest {
         assertTrue(notBefore.after(inFuture(-1)));
         assertTrue(notBefore.before(inFuture(1)));
 
-        assertEquals(dn, x509Certificate.getIssuerDN().getName());
+        assertEquals(dn, x509Certificate.getIssuerX500Principal().getName());
         assertEquals(signingAlgorithm, x509Certificate.getSigAlgName());
         assertEquals(keyPairAlgorithm, x509Certificate.getPublicKey().getAlgorithm());
 
@@ -139,12 +187,12 @@ public class TlsHelperTest {
         X509Certificate issuer = loadCertificate(new InputStreamReader(getClass().getClassLoader().getResourceAsStream("rootCert.crt")));
         KeyPair issuerKeyPair = loadKeyPair(new InputStreamReader(getClass().getClassLoader().getResourceAsStream("rootCert.key")));
 
-        String dn = "CN=testIssued,O=testOrg";
+        String dn = "CN=testIssued, O=testOrg";
 
         KeyPair keyPair = TlsHelper.generateKeyPair(keyPairAlgorithm, keySize);
         X509Certificate x509Certificate = CertificateUtils.generateIssuedCertificate(dn, keyPair.getPublic(), issuer, issuerKeyPair, signingAlgorithm, days);
-        assertEquals(dn, x509Certificate.getSubjectDN().toString());
-        assertEquals(issuer.getSubjectDN().toString(), x509Certificate.getIssuerDN().toString());
+        assertEquals(dn, x509Certificate.getSubjectX500Principal().toString());
+        assertEquals(issuer.getSubjectX500Principal().toString(), x509Certificate.getIssuerX500Principal().toString());
         assertEquals(keyPair.getPublic(), x509Certificate.getPublicKey());
 
         Date notAfter = x509Certificate.getNotAfter();
@@ -160,4 +208,83 @@ public class TlsHelperTest {
 
         x509Certificate.verify(issuerKeyPair.getPublic());
     }
+
+    @Test
+    public void testWriteKeyStoreSuccess() throws IOException, GeneralSecurityException {
+        setUnlimitedCrypto(false);
+        String testPassword = "testPassword";
+        assertEquals(testPassword, TlsHelper.writeKeyStore(keyStore, outputStreamFactory, file, testPassword, false));
+        verify(keyStoreSpi, times(1)).engineStore(eq(tmpFileOutputStream), AdditionalMatchers.aryEq(testPassword.toCharArray()));
+    }
+
+    @Test
+    public void testWriteKeyStoreFailure() throws IOException, GeneralSecurityException {
+        setUnlimitedCrypto(false);
+        String testPassword = "testPassword";
+        IOException ioException = new IOException("Fail");
+        doThrow(ioException).when(keyStoreSpi).engineStore(eq(tmpFileOutputStream), AdditionalMatchers.aryEq(testPassword.toCharArray()));
+        try {
+            TlsHelper.writeKeyStore(keyStore, outputStreamFactory, file, testPassword, true);
+            fail("Expected " + ioException);
+        } catch (IOException e) {
+            assertEquals(ioException, e);
+        }
+    }
+
+    @Test
+    public void testWriteKeyStoreTruncate() throws IOException, GeneralSecurityException {
+        setUnlimitedCrypto(false);
+        String testPassword = "testPassword";
+        String truncatedPassword = testPassword.substring(0, 7);
+        IOException ioException = new IOException(TlsHelper.ILLEGAL_KEY_SIZE);
+        doThrow(ioException).when(keyStoreSpi).engineStore(eq(tmpFileOutputStream), AdditionalMatchers.aryEq(testPassword.toCharArray()));
+        assertEquals(truncatedPassword, TlsHelper.writeKeyStore(keyStore, outputStreamFactory, file, testPassword, true));
+        verify(keyStoreSpi, times(1)).engineStore(eq(tmpFileOutputStream), AdditionalMatchers.aryEq(testPassword.toCharArray()));
+        verify(keyStoreSpi, times(1)).engineStore(eq(tmpFileOutputStream), AdditionalMatchers.aryEq(truncatedPassword.toCharArray()));
+    }
+
+    @Test
+    public void testWriteKeyStoreUnlimitedWontTruncate() throws GeneralSecurityException, IOException {
+        setUnlimitedCrypto(true);
+        String testPassword = "testPassword";
+        IOException ioException = new IOException(TlsHelper.ILLEGAL_KEY_SIZE);
+        doThrow(ioException).when(keyStoreSpi).engineStore(eq(tmpFileOutputStream), AdditionalMatchers.aryEq(testPassword.toCharArray()));
+        try {
+            TlsHelper.writeKeyStore(keyStore, outputStreamFactory, file, testPassword, true);
+            fail("Expected " + ioException);
+        } catch (IOException e) {
+            assertEquals(ioException, e);
+        }
+    }
+
+    @Test
+    public void testWriteKeyStoreNoTruncate() throws IOException, GeneralSecurityException {
+        setUnlimitedCrypto(false);
+        String testPassword = "testPassword";
+        IOException ioException = new IOException(TlsHelper.ILLEGAL_KEY_SIZE);
+        doThrow(ioException).when(keyStoreSpi).engineStore(eq(tmpFileOutputStream), AdditionalMatchers.aryEq(testPassword.toCharArray()));
+        try {
+            TlsHelper.writeKeyStore(keyStore, outputStreamFactory, file, testPassword, false);
+            fail("Expected " + GeneralSecurityException.class);
+        } catch (GeneralSecurityException e) {
+            assertTrue("Expected exception to contain " + TlsHelper.JCE_URL, e.getMessage().contains(TlsHelper.JCE_URL));
+        }
+    }
+
+    @Test
+    public void testWriteKeyStoreTruncateFailure() throws IOException, GeneralSecurityException {
+        setUnlimitedCrypto(false);
+        String testPassword = "testPassword";
+        String truncatedPassword = testPassword.substring(0, 7);
+        IOException ioException = new IOException(TlsHelper.ILLEGAL_KEY_SIZE);
+        IOException ioException2 = new IOException(TlsHelper.ILLEGAL_KEY_SIZE);
+        doThrow(ioException).when(keyStoreSpi).engineStore(eq(tmpFileOutputStream), AdditionalMatchers.aryEq(testPassword.toCharArray()));
+        doThrow(ioException2).when(keyStoreSpi).engineStore(eq(tmpFileOutputStream), AdditionalMatchers.aryEq(truncatedPassword.toCharArray()));
+        try {
+            TlsHelper.writeKeyStore(keyStore, outputStreamFactory, file, testPassword, true);
+            fail("Expected " + ioException2);
+        } catch (IOException e) {
+            assertEquals(ioException2, e);
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/fa5da543/nifi-toolkit/nifi-toolkit-tls/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-tls/src/test/resources/log4j.properties b/nifi-toolkit/nifi-toolkit-tls/src/test/resources/log4j.properties
new file mode 100644
index 0000000..fc2aaf1
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-tls/src/test/resources/log4j.properties
@@ -0,0 +1,22 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+log4j.rootLogger=INFO,console
+
+log4j.appender.console=org.apache.log4j.ConsoleAppender
+log4j.appender.console.layout=org.apache.log4j.PatternLayout
+log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{2}: %m%n
\ No newline at end of file


[2/2] nifi git commit: NIFI-2526 - DN order, multiple standalone runs, client certificates - Logic for sorting DN, reversing X500Names before using them to generate certificate - Logging reordered dn - Accounting for limited crypto pkcs12, allowing passw

Posted by bb...@apache.org.
NIFI-2526 - DN order, multiple standalone runs, client certificates
- Logic for sorting DN, reversing X500Names before using them to generate certificate
- Logging reordered dn
- Accounting for limited crypto pkcs12, allowing password specification for client certificates
- Updating tests to work with or without jce unlimited
- Loading keystore for test in try-with

This closes #824.

Signed-off-by: Bryan Bende <bb...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/fa5da543
Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/fa5da543
Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/fa5da543

Branch: refs/heads/master
Commit: fa5da543e6334aec363f5fb0c470a7e0b682042d
Parents: fd0dd51
Author: Bryan Rosander <br...@gmail.com>
Authored: Tue Aug 9 11:11:21 2016 -0400
Committer: Bryan Bende <bb...@apache.org>
Committed: Mon Aug 15 10:21:34 2016 -0400

----------------------------------------------------------------------
 .../nifi/security/util/CertificateUtils.java    |  99 ++++++++++-
 .../security/util/CertificateUtilsTest.groovy   |  26 ++-
 .../nifi/toolkit/tls/commandLine/ExitCode.java  |   1 -
 .../tls/configuration/StandaloneConfig.java     | 125 ++++++++++++++
 .../toolkit/tls/configuration/TlsConfig.java    |   3 +-
 .../toolkit/tls/manager/BaseTlsManager.java     |  14 +-
 .../manager/TlsCertificateAuthorityManager.java |   2 +-
 .../toolkit/tls/manager/TlsClientManager.java   |  13 +-
 .../TlsCertificateSigningRequestPerformer.java  |   8 +-
 .../TlsCertificateAuthorityServiceHandler.java  |   6 +-
 .../tls/standalone/TlsToolkitStandalone.java    | 168 +++++++++++++++----
 .../TlsToolkitStandaloneCommandLine.java        | 118 +++++++------
 .../apache/nifi/toolkit/tls/util/TlsHelper.java |  99 ++++++++++-
 .../service/TlsCertificateAuthorityTest.java    |   4 +-
 ...sCertificateAuthorityServiceHandlerTest.java |   2 +-
 .../TlsToolkitStandaloneCommandLineTest.java    | 107 ++++++++----
 .../standalone/TlsToolkitStandaloneTest.java    |  98 +++++++++--
 .../nifi/toolkit/tls/util/TlsHelperTest.java    | 151 +++++++++++++++--
 .../src/test/resources/log4j.properties         |  22 +++
 19 files changed, 882 insertions(+), 184 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/fa5da543/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java
----------------------------------------------------------------------
diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java
index 2049718..760e82e 100644
--- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java
+++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java
@@ -17,7 +17,11 @@
 package org.apache.nifi.security.util;
 
 import org.apache.commons.lang3.StringUtils;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.x500.AttributeTypeAndValue;
+import org.bouncycastle.asn1.x500.RDN;
 import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x500.style.BCStyle;
 import org.bouncycastle.asn1.x509.BasicConstraints;
 import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
 import org.bouncycastle.asn1.x509.Extension;
@@ -57,16 +61,37 @@ import java.security.cert.CertificateFactory;
 import java.security.cert.CertificateParsingException;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
 public final class CertificateUtils {
-
     private static final Logger logger = LoggerFactory.getLogger(CertificateUtils.class);
     private static final String PEER_NOT_AUTHENTICATED_MSG = "peer not authenticated";
 
+    private static final Map<ASN1ObjectIdentifier, Integer> dnOrderMap = createDnOrderMap();
+
+    private static Map<ASN1ObjectIdentifier, Integer> createDnOrderMap() {
+        Map<ASN1ObjectIdentifier, Integer> orderMap = new HashMap<>();
+        int count = 0;
+        orderMap.put(BCStyle.CN, count++);
+        orderMap.put(BCStyle.L, count++);
+        orderMap.put(BCStyle.ST, count++);
+        orderMap.put(BCStyle.O, count++);
+        orderMap.put(BCStyle.OU, count++);
+        orderMap.put(BCStyle.C, count++);
+        orderMap.put(BCStyle.STREET, count++);
+        orderMap.put(BCStyle.DC, count++);
+        orderMap.put(BCStyle.UID, count++);
+        return Collections.unmodifiableMap(orderMap);
+    }
+
     public enum ClientAuth {
         NONE(0, "none"),
         WANT(1, "want"),
@@ -350,6 +375,70 @@ public final class CertificateUtils {
     }
 
     /**
+     * Reorders DN to the order the elements appear in the RFC 2253 table
+     *
+     * https://www.ietf.org/rfc/rfc2253.txt
+     *
+     * String  X.500 AttributeType
+     * ------------------------------
+     * CN      commonName
+     * L       localityName
+     * ST      stateOrProvinceName
+     * O       organizationName
+     * OU      organizationalUnitName
+     * C       countryName
+     * STREET  streetAddress
+     * DC      domainComponent
+     * UID     userid
+     *
+     * @param dn a possibly unordered DN
+     * @return the ordered dn
+     */
+    public static String reorderDn(String dn) {
+        RDN[] rdNs = new X500Name(dn).getRDNs();
+        Arrays.sort(rdNs, new Comparator<RDN>() {
+            @Override
+            public int compare(RDN o1, RDN o2) {
+                AttributeTypeAndValue o1First = o1.getFirst();
+                AttributeTypeAndValue o2First = o2.getFirst();
+
+                ASN1ObjectIdentifier o1Type = o1First.getType();
+                ASN1ObjectIdentifier o2Type = o2First.getType();
+
+                Integer o1Rank = dnOrderMap.get(o1Type);
+                Integer o2Rank = dnOrderMap.get(o2Type);
+                if (o1Rank == null) {
+                    if (o2Rank == null) {
+                        int idComparison = o1Type.getId().compareTo(o2Type.getId());
+                        if (idComparison != 0) {
+                            return idComparison;
+                        }
+                        return String.valueOf(o1Type).compareTo(String.valueOf(o2Type));
+                    }
+                    return 1;
+                } else if (o2Rank == null) {
+                    return -1;
+                }
+                return o1Rank - o2Rank;
+            }
+        });
+        return new X500Name(rdNs).toString();
+    }
+
+    /**
+     * Reverses the X500Name in order make the certificate be in the right order
+     * [see http://stackoverflow.com/questions/7567837/attributes-reversed-in-certificate-subject-and-issuer/12645265]
+     *
+     * @param x500Name the X500Name created with the intended order
+     * @return the X500Name reversed
+     */
+    private static X500Name reverseX500Name(X500Name x500Name) {
+        List<RDN> rdns = Arrays.asList(x500Name.getRDNs());
+        Collections.reverse(rdns);
+        return new X500Name(rdns.toArray(new RDN[rdns.size()]));
+    }
+
+    /**
      * Generates a self-signed {@link X509Certificate} suitable for use as a Certificate Authority.
      *
      * @param keyPair                 the {@link KeyPair} to generate the {@link X509Certificate} for
@@ -368,10 +457,10 @@ public final class CertificateUtils {
             Date endDate = new Date(startDate.getTime() + TimeUnit.DAYS.toMillis(certificateDurationDays));
 
             X509v3CertificateBuilder certBuilder = new X509v3CertificateBuilder(
-                    new X500Name(dn),
+                    reverseX500Name(new X500Name(dn)),
                     BigInteger.valueOf(System.currentTimeMillis()),
                     startDate, endDate,
-                    new X500Name(dn),
+                    reverseX500Name(new X500Name(dn)),
                     subPubKeyInfo);
 
             // Set certificate extensions
@@ -417,10 +506,10 @@ public final class CertificateUtils {
             Date endDate = new Date(startDate.getTime() + TimeUnit.DAYS.toMillis(days));
 
             X509v3CertificateBuilder certBuilder = new X509v3CertificateBuilder(
-                    new X500Name(issuer.getSubjectDN().getName()),
+                    reverseX500Name(new X500Name(issuer.getSubjectX500Principal().getName())),
                     BigInteger.valueOf(System.currentTimeMillis()),
                     startDate, endDate,
-                    new X500Name(dn),
+                    reverseX500Name(new X500Name(dn)),
                     subPubKeyInfo);
 
             certBuilder.addExtension(Extension.subjectKeyIdentifier, false, new JcaX509ExtensionUtils().createSubjectKeyIdentifier(publicKey));

http://git-wip-us.apache.org/repos/asf/nifi/blob/fa5da543/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/CertificateUtilsTest.groovy
----------------------------------------------------------------------
diff --git a/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/CertificateUtilsTest.groovy b/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/CertificateUtilsTest.groovy
index 7f93f51..6727364 100644
--- a/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/CertificateUtilsTest.groovy
+++ b/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/CertificateUtilsTest.groovy
@@ -443,7 +443,7 @@ class CertificateUtilsTest extends GroovyTestCase {
         assertTrue(notBefore.after(inFuture(-1)));
         assertTrue(notBefore.before(inFuture(1)));
 
-        assertEquals(dn, x509Certificate.getIssuerDN().getName());
+        assertEquals(dn, x509Certificate.getIssuerX500Principal().getName());
         assertEquals(SIGNATURE_ALGORITHM.toUpperCase(), x509Certificate.getSigAlgName().toUpperCase());
         assertEquals("RSA", x509Certificate.getPublicKey().getAlgorithm());
 
@@ -458,12 +458,12 @@ class CertificateUtilsTest extends GroovyTestCase {
         KeyPair issuerKeyPair = generateKeyPair();
         X509Certificate issuer = CertificateUtils.generateSelfSignedX509Certificate(issuerKeyPair, "CN=testCa,O=testOrg", SIGNATURE_ALGORITHM, days);
 
-        String dn = "CN=testIssued,O=testOrg";
+        String dn = "CN=testIssued, O=testOrg";
 
         KeyPair keyPair = generateKeyPair();
         X509Certificate x509Certificate = CertificateUtils.generateIssuedCertificate(dn, keyPair.getPublic(), issuer, issuerKeyPair, SIGNATURE_ALGORITHM, days);
-        assertEquals(dn, x509Certificate.getSubjectDN().toString());
-        assertEquals(issuer.getSubjectDN().toString(), x509Certificate.getIssuerDN().toString());
+        assertEquals(dn, x509Certificate.getSubjectX500Principal().toString());
+        assertEquals(issuer.getSubjectX500Principal().toString(), x509Certificate.getIssuerX500Principal().toString());
         assertEquals(keyPair.getPublic(), x509Certificate.getPublicKey());
 
         Date notAfter = x509Certificate.getNotAfter();
@@ -479,4 +479,22 @@ class CertificateUtilsTest extends GroovyTestCase {
 
         x509Certificate.verify(issuerKeyPair.getPublic());
     }
+
+    @Test
+    public void reorderShouldPutElementsInCorrectOrder() {
+        String cn = "CN=testcn";
+        String l = "L=testl";
+        String st = "ST=testst";
+        String o = "O=testo";
+        String ou = "OU=testou";
+        String c = "C=testc";
+        String street = "STREET=teststreet";
+        String dc = "DC=testdc";
+        String uid = "UID=testuid";
+        String surname = "SURNAME=testsurname";
+        String initials = "INITIALS=testinitials";
+        String givenName = "GIVENNAME=testgivenname";
+        assertEquals("$cn,$l,$st,$o,$ou,$c,$street,$dc,$uid,$surname,$givenName,$initials".toString(),
+                CertificateUtils.reorderDn("$surname,$st,$o,$initials,$givenName,$uid,$street,$c,$cn,$ou,$l,$dc"));
+    }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/fa5da543/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/commandLine/ExitCode.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/commandLine/ExitCode.java b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/commandLine/ExitCode.java
index b99b1af..f06ac2d 100644
--- a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/commandLine/ExitCode.java
+++ b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/commandLine/ExitCode.java
@@ -24,7 +24,6 @@ public enum ExitCode {
     SERVICE_ERROR,
     ERROR_PARSING_COMMAND_LINE,
     ERROR_GENERATING_CONFIG,
-    ERROR_SAME_KEY_AND_KEY_PASSWORD,
     ERROR_INCORRECT_NUMBER_OF_PASSWORDS,
     ERROR_PARSING_INT_ARG,
     ERROR_TOKEN_ARG_EMPTY,

http://git-wip-us.apache.org/repos/asf/nifi/blob/fa5da543/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/StandaloneConfig.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..342e1e5
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/StandaloneConfig.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.nifi.toolkit.tls.configuration;
+
+import org.apache.nifi.toolkit.tls.properties.NiFiPropertiesWriterFactory;
+
+import java.io.File;
+import java.util.List;
+
+public class StandaloneConfig extends TlsConfig {
+    private File baseDir;
+    private NiFiPropertiesWriterFactory niFiPropertiesWriterFactory;
+    private List<String> hostnames;
+    private List<String> keyStorePasswords;
+    private List<String> keyPasswords;
+    private List<String> trustStorePasswords;
+    private List<String> clientDns;
+    private List<String> clientPasswords;
+    private boolean clientPasswordsGenerated;
+    private int httpsPort;
+    private boolean overwrite;
+
+    public List<String> getClientDns() {
+        return clientDns;
+    }
+
+    public void setClientDns(List<String> clientDns) {
+        this.clientDns = clientDns;
+    }
+
+    public boolean isOverwrite() {
+        return overwrite;
+    }
+
+    public void setOverwrite(boolean overwrite) {
+        this.overwrite = overwrite;
+    }
+
+    public File getBaseDir() {
+        return baseDir;
+    }
+
+    public void setBaseDir(File baseDir) {
+        this.baseDir = baseDir;
+    }
+
+    public NiFiPropertiesWriterFactory getNiFiPropertiesWriterFactory() {
+        return niFiPropertiesWriterFactory;
+    }
+
+    public void setNiFiPropertiesWriterFactory(NiFiPropertiesWriterFactory niFiPropertiesWriterFactory) {
+        this.niFiPropertiesWriterFactory = niFiPropertiesWriterFactory;
+    }
+
+    public List<String> getHostnames() {
+        return hostnames;
+    }
+
+    public void setHostnames(List<String> hostnames) {
+        this.hostnames = hostnames;
+    }
+
+    public List<String> getKeyStorePasswords() {
+        return keyStorePasswords;
+    }
+
+    public void setKeyStorePasswords(List<String> keyStorePasswords) {
+        this.keyStorePasswords = keyStorePasswords;
+    }
+
+    public List<String> getKeyPasswords() {
+        return keyPasswords;
+    }
+
+    public void setKeyPasswords(List<String> keyPasswords) {
+        this.keyPasswords = keyPasswords;
+    }
+
+    public List<String> getTrustStorePasswords() {
+        return trustStorePasswords;
+    }
+
+    public void setTrustStorePasswords(List<String> trustStorePasswords) {
+        this.trustStorePasswords = trustStorePasswords;
+    }
+
+    public List<String> getClientPasswords() {
+        return clientPasswords;
+    }
+
+    public void setClientPasswords(List<String> clientPasswords) {
+        this.clientPasswords = clientPasswords;
+    }
+
+    public int getHttpsPort() {
+        return httpsPort;
+    }
+
+    public void setHttpsPort(int httpsPort) {
+        this.httpsPort = httpsPort;
+    }
+
+    public boolean isClientPasswordsGenerated() {
+        return clientPasswordsGenerated;
+    }
+
+    public void setClientPasswordsGenerated(boolean clientPasswordsGenerated) {
+        this.clientPasswordsGenerated = clientPasswordsGenerated;
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/fa5da543/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/TlsConfig.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/TlsConfig.java b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/TlsConfig.java
index 819b41b..8aa3e40 100644
--- a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/TlsConfig.java
+++ b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/TlsConfig.java
@@ -17,6 +17,7 @@
 
 package org.apache.nifi.toolkit.tls.configuration;
 
+import org.apache.nifi.security.util.CertificateUtils;
 import org.apache.nifi.util.StringUtils;
 
 public class TlsConfig {
@@ -43,7 +44,7 @@ public class TlsConfig {
     private int port = DEFAULT_PORT;
 
     public static String calcDefaultDn(String hostname) {
-        return "CN=" + hostname + ",OU=NIFI";
+        return CertificateUtils.reorderDn("CN=" + hostname + ",OU=NIFI");
     }
 
     public int getPort() {

http://git-wip-us.apache.org/repos/asf/nifi/blob/fa5da543/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/manager/BaseTlsManager.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/manager/BaseTlsManager.java b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/manager/BaseTlsManager.java
index 5a5e049..6215fc0 100644
--- a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/manager/BaseTlsManager.java
+++ b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/manager/BaseTlsManager.java
@@ -22,6 +22,7 @@ import org.apache.nifi.toolkit.tls.manager.writer.ConfigurationWriter;
 import org.apache.nifi.toolkit.tls.util.InputStreamFactory;
 import org.apache.nifi.toolkit.tls.util.OutputStreamFactory;
 import org.apache.nifi.toolkit.tls.util.PasswordUtil;
+import org.apache.nifi.toolkit.tls.util.TlsHelper;
 import org.apache.nifi.util.StringUtils;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 
@@ -29,7 +30,6 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.OutputStream;
 import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.security.KeyStore;
@@ -38,7 +38,6 @@ import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchProviderException;
 import java.security.UnrecoverableEntryException;
 import java.security.cert.Certificate;
-import java.security.cert.CertificateException;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -50,6 +49,7 @@ public class BaseTlsManager {
     private final KeyStore keyStore;
     private final List<ConfigurationWriter<TlsConfig>> configurationWriters;
     private boolean differentKeyAndKeyStorePassword = false;
+    private boolean keyStorePasswordGenerated = false;
 
     public BaseTlsManager(TlsConfig tlsConfig) throws GeneralSecurityException, IOException {
         this(tlsConfig, new PasswordUtil(), FileInputStream::new);
@@ -107,13 +107,14 @@ public class BaseTlsManager {
         String result = tlsConfig.getKeyStorePassword();
         if (StringUtils.isEmpty(result)) {
             result = passwordUtil.generatePassword();
+            keyStorePasswordGenerated = true;
             tlsConfig.setKeyStorePassword(result);
         }
         return result;
     }
 
     private KeyStore getInstance(String keyStoreType) throws KeyStoreException, NoSuchProviderException {
-        if (PKCS_12.equals(keyStoreType)) {
+        if (PKCS_12.equalsIgnoreCase(keyStoreType)) {
             return KeyStore.getInstance(keyStoreType, BouncyCastleProvider.PROVIDER_NAME);
         } else {
             return KeyStore.getInstance(keyStoreType);
@@ -133,12 +134,9 @@ public class BaseTlsManager {
         return result;
     }
 
-    public void write(OutputStreamFactory outputStreamFactory) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException {
+    public void write(OutputStreamFactory outputStreamFactory) throws IOException, GeneralSecurityException {
         String keyStorePassword = getKeyStorePassword();
-
-        try (OutputStream outputStream = outputStreamFactory.create(new File(tlsConfig.getKeyStore()))) {
-            keyStore.store(outputStream, keyStorePassword.toCharArray());
-        }
+        tlsConfig.setKeyStorePassword(TlsHelper.writeKeyStore(keyStore, outputStreamFactory, new File(tlsConfig.getKeyStore()), keyStorePassword, keyStorePasswordGenerated));
 
         for (ConfigurationWriter<TlsConfig> configurationWriter : configurationWriters) {
             configurationWriter.write(tlsConfig);

http://git-wip-us.apache.org/repos/asf/nifi/blob/fa5da543/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/manager/TlsCertificateAuthorityManager.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/manager/TlsCertificateAuthorityManager.java b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/manager/TlsCertificateAuthorityManager.java
index 39faf28..2fa0534 100644
--- a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/manager/TlsCertificateAuthorityManager.java
+++ b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/manager/TlsCertificateAuthorityManager.java
@@ -38,7 +38,7 @@ public class TlsCertificateAuthorityManager extends BaseTlsManager {
         if (entry == null) {
             TlsConfig tlsConfig = getTlsConfig();
             KeyPair keyPair = TlsHelper.generateKeyPair(tlsConfig.getKeyPairAlgorithm(), tlsConfig.getKeySize());
-            X509Certificate caCert = CertificateUtils.generateSelfSignedX509Certificate(keyPair, tlsConfig.getDn(), tlsConfig.getSigningAlgorithm(), tlsConfig.getDays());
+            X509Certificate caCert = CertificateUtils.generateSelfSignedX509Certificate(keyPair, CertificateUtils.reorderDn(tlsConfig.getDn()), tlsConfig.getSigningAlgorithm(), tlsConfig.getDays());
             entry = addPrivateKeyToKeyStore(keyPair, TlsToolkitStandalone.NIFI_KEY, caCert);
         } else if (!KeyStore.PrivateKeyEntry.class.isInstance(entry)) {
             throw new IOException("Expected " + TlsToolkitStandalone.NIFI_KEY + " alias to contain a private key entry");

http://git-wip-us.apache.org/repos/asf/nifi/blob/fa5da543/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/manager/TlsClientManager.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/manager/TlsClientManager.java b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/manager/TlsClientManager.java
index 52ea5db..3113701 100644
--- a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/manager/TlsClientManager.java
+++ b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/manager/TlsClientManager.java
@@ -22,6 +22,7 @@ import org.apache.nifi.toolkit.tls.manager.writer.ConfigurationWriter;
 import org.apache.nifi.toolkit.tls.util.InputStreamFactory;
 import org.apache.nifi.toolkit.tls.util.OutputStreamFactory;
 import org.apache.nifi.toolkit.tls.util.PasswordUtil;
+import org.apache.nifi.toolkit.tls.util.TlsHelper;
 import org.apache.nifi.util.StringUtils;
 import org.bouncycastle.openssl.jcajce.JcaMiscPEMGenerator;
 import org.bouncycastle.util.io.pem.PemWriter;
@@ -34,10 +35,8 @@ import java.io.OutputStreamWriter;
 import java.security.GeneralSecurityException;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
 import java.security.UnrecoverableEntryException;
 import java.security.cert.Certificate;
-import java.security.cert.CertificateException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
@@ -73,18 +72,18 @@ public class TlsClientManager extends BaseTlsManager {
     }
 
     @Override
-    public void write(OutputStreamFactory outputStreamFactory) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException {
+    public void write(OutputStreamFactory outputStreamFactory) throws IOException, GeneralSecurityException {
         super.write(outputStreamFactory);
 
         String trustStorePassword = tlsClientConfig.getTrustStorePassword();
+        boolean trustStorePasswordGenerated = false;
         if (StringUtils.isEmpty(trustStorePassword)) {
             trustStorePassword = getPasswordUtil().generatePassword();
-            tlsClientConfig.setTrustStorePassword(trustStorePassword);
+            trustStorePasswordGenerated = true;
         }
 
-        try (OutputStream outputStream = outputStreamFactory.create(new File(tlsClientConfig.getTrustStore()))) {
-            trustStore.store(outputStream, trustStorePassword.toCharArray());
-        }
+        trustStorePassword = TlsHelper.writeKeyStore(trustStore, outputStreamFactory, new File(tlsClientConfig.getTrustStore()), trustStorePassword, trustStorePasswordGenerated);
+        tlsClientConfig.setTrustStorePassword(trustStorePassword);
 
         for (ConfigurationWriter<TlsClientConfig> configurationWriter : configurationWriters) {
             configurationWriter.write(tlsClientConfig);

http://git-wip-us.apache.org/repos/asf/nifi/blob/fa5da543/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/service/client/TlsCertificateSigningRequestPerformer.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/service/client/TlsCertificateSigningRequestPerformer.java b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/service/client/TlsCertificateSigningRequestPerformer.java
index 2c65964..889f217 100644
--- a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/service/client/TlsCertificateSigningRequestPerformer.java
+++ b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/service/client/TlsCertificateSigningRequestPerformer.java
@@ -28,6 +28,7 @@ import org.apache.http.entity.ByteArrayEntity;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClientBuilder;
 import org.apache.http.ssl.SSLContextBuilder;
+import org.apache.nifi.security.util.CertificateUtils;
 import org.apache.nifi.toolkit.tls.configuration.TlsClientConfig;
 import org.apache.nifi.toolkit.tls.service.dto.TlsCertificateAuthorityRequest;
 import org.apache.nifi.toolkit.tls.service.dto.TlsCertificateAuthorityResponse;
@@ -38,6 +39,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
+import java.io.StringReader;
 import java.nio.charset.StandardCharsets;
 import java.security.KeyPair;
 import java.security.MessageDigest;
@@ -73,7 +75,7 @@ public class TlsCertificateSigningRequestPerformer {
     private TlsCertificateSigningRequestPerformer(Supplier<HttpClientBuilder> httpClientBuilderSupplier, String caHostname, String dn, String token, int port, String signingAlgorithm) {
         this.httpClientBuilderSupplier = httpClientBuilderSupplier;
         this.caHostname = caHostname;
-        this.dn = dn;
+        this.dn = CertificateUtils.reorderDn(dn);
         this.token = token;
         this.port = port;
         this.objectMapper = new ObjectMapper();
@@ -141,10 +143,10 @@ public class TlsCertificateSigningRequestPerformer {
             if (!tlsCertificateAuthorityResponse.hasCertificate()) {
                 throw new IOException(EXPECTED_RESPONSE_TO_CONTAIN_CERTIFICATE);
             }
-            X509Certificate x509Certificate = TlsHelper.parseCertificate(tlsCertificateAuthorityResponse.getPemEncodedCertificate());
+            X509Certificate x509Certificate = TlsHelper.parseCertificate(new StringReader(tlsCertificateAuthorityResponse.getPemEncodedCertificate()));
             x509Certificate.verify(caCertificate.getPublicKey());
             if (logger.isInfoEnabled()) {
-                logger.info("Got certificate with dn " + x509Certificate.getSubjectDN());
+                logger.info("Got certificate with dn " + x509Certificate.getSubjectX500Principal());
             }
             return new X509Certificate[]{x509Certificate, caCertificate};
         } catch (IOException e) {

http://git-wip-us.apache.org/repos/asf/nifi/blob/fa5da543/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/service/server/TlsCertificateAuthorityServiceHandler.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/service/server/TlsCertificateAuthorityServiceHandler.java b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/service/server/TlsCertificateAuthorityServiceHandler.java
index fb83498..27d995e 100644
--- a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/service/server/TlsCertificateAuthorityServiceHandler.java
+++ b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/service/server/TlsCertificateAuthorityServiceHandler.java
@@ -82,7 +82,11 @@ public class TlsCertificateAuthorityServiceHandler extends AbstractHandler {
             byte[] expectedHmac = TlsHelper.calculateHMac(token, jcaPKCS10CertificationRequest.getPublicKey());
 
             if (MessageDigest.isEqual(expectedHmac, tlsCertificateAuthorityRequest.getHmac())) {
-                X509Certificate x509Certificate = CertificateUtils.generateIssuedCertificate(jcaPKCS10CertificationRequest.getSubject().toString(),
+                String dn = jcaPKCS10CertificationRequest.getSubject().toString();
+                if (logger.isInfoEnabled()) {
+                    logger.info("Received CSR with DN " + dn);
+                }
+                X509Certificate x509Certificate = CertificateUtils.generateIssuedCertificate(dn,
                         jcaPKCS10CertificationRequest.getPublicKey(), caCert, keyPair, signingAlgorithm, days);
                 writeResponse(objectMapper, request, response, new TlsCertificateAuthorityResponse(TlsHelper.calculateHMac(token, caCert.getPublicKey()),
                         TlsHelper.pemEncodeJcaObject(x509Certificate)), Response.SC_OK);

http://git-wip-us.apache.org/repos/asf/nifi/blob/fa5da543/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandalone.java
----------------------------------------------------------------------
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 0136e64..c91e7fa 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,14 +18,17 @@
 package org.apache.nifi.toolkit.tls.standalone;
 
 import org.apache.nifi.security.util.CertificateUtils;
+import org.apache.nifi.toolkit.tls.configuration.StandaloneConfig;
 import org.apache.nifi.toolkit.tls.configuration.TlsClientConfig;
 import org.apache.nifi.toolkit.tls.configuration.TlsConfig;
+import org.apache.nifi.toolkit.tls.manager.BaseTlsManager;
 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.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.openssl.jcajce.JcaMiscPEMGenerator;
 import org.bouncycastle.util.io.pem.PemWriter;
 import org.slf4j.Logger;
@@ -33,11 +36,14 @@ import org.slf4j.LoggerFactory;
 
 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.cert.Certificate;
 import java.security.cert.X509Certificate;
 import java.util.List;
 
@@ -57,17 +63,8 @@ public class TlsToolkitStandalone {
         this.outputStreamFactory = outputStreamFactory;
     }
 
-    public void createNifiKeystoresAndTrustStores(File baseDir, TlsConfig tlsConfig, NiFiPropertiesWriterFactory niFiPropertiesWriterFactory, List<String> hostnames, List<String> keyStorePasswords,
-                                                  List<String> keyPasswords, List<String> trustStorePasswords, int httpsPort) throws GeneralSecurityException, IOException {
-        String signingAlgorithm = tlsConfig.getSigningAlgorithm();
-        int days = tlsConfig.getDays();
-        String keyPairAlgorithm = tlsConfig.getKeyPairAlgorithm();
-        int keySize = tlsConfig.getKeySize();
-        TlsCertificateAuthorityManager tlsCertificateAuthorityManager = new TlsCertificateAuthorityManager(tlsConfig);
-        KeyStore.PrivateKeyEntry privateKeyEntry = tlsCertificateAuthorityManager.getOrGenerateCertificateAuthority();
-        X509Certificate certificate = (X509Certificate) privateKeyEntry.getCertificateChain()[0];
-        KeyPair caKeyPair = new KeyPair(certificate.getPublicKey(), privateKeyEntry.getPrivateKey());
-
+    public void createNifiKeystoresAndTrustStores(StandaloneConfig standaloneConfig) throws GeneralSecurityException, IOException {
+        File baseDir = standaloneConfig.getBaseDir();
         if (!baseDir.exists() && !baseDir.mkdirs()) {
             throw new IOException(baseDir + " doesn't exist and unable to create it.");
         }
@@ -76,48 +73,107 @@ public class TlsToolkitStandalone {
             throw new IOException("Expected directory to output to");
         }
 
-        if (logger.isInfoEnabled()) {
-            logger.info("Running standalone certificate generation with output directory " + baseDir + " and hostnames " + hostnames);
-        }
+        String signingAlgorithm = standaloneConfig.getSigningAlgorithm();
+        int days = standaloneConfig.getDays();
+        String keyPairAlgorithm = standaloneConfig.getKeyPairAlgorithm();
+        int keySize = standaloneConfig.getKeySize();
 
         File nifiCert = new File(baseDir, NIFI_CERT + ".pem");
-        if (nifiCert.exists()) {
-            throw new IOException(nifiCert.getAbsolutePath() + " exists already.");
-        }
-
         File nifiKey = new File(baseDir, NIFI_KEY + ".key");
-        if (nifiKey.exists()) {
-            throw new IOException(nifiKey.getAbsolutePath() + " exists already.");
+
+        X509Certificate certificate;
+        KeyPair caKeyPair;
+
+        List<String> hostnames = standaloneConfig.getHostnames();
+        if (logger.isInfoEnabled()) {
+            logger.info("Running standalone certificate generation with output directory " + baseDir);
         }
+        if (nifiCert.exists()) {
+            if (!nifiKey.exists()) {
+                throw new IOException(nifiCert + " exists already, but " + nifiKey + " does not, we need both certificate and key to continue with an existing CA.");
+            }
+            try (FileReader pemEncodedCertificate = new FileReader(nifiCert)) {
+                certificate = TlsHelper.parseCertificate(pemEncodedCertificate);
+            }
+            try (FileReader pemEncodedKeyPair = new FileReader(nifiKey)) {
+                caKeyPair = TlsHelper.parseKeyPair(pemEncodedKeyPair);
+            }
 
-        for (String hostname : hostnames) {
-            File hostDirectory = new File(baseDir, hostname);
-            if (hostDirectory.exists()) {
-                throw new IOException("Output destination for host " + hostname + " (" + hostDirectory.getAbsolutePath() + ") exists already.");
+            certificate.verify(caKeyPair.getPublic());
+            if (!caKeyPair.getPublic().equals(certificate.getPublicKey())) {
+                throw new IOException("Expected " + nifiKey + " to correspond to CA certificate at " + nifiCert);
             }
-        }
 
-        try (PemWriter pemWriter = new PemWriter(new OutputStreamWriter(outputStreamFactory.create(nifiCert)))) {
-            pemWriter.writeObject(new JcaMiscPEMGenerator(certificate));
-        }
+            if (logger.isInfoEnabled()) {
+                logger.info("Using existing CA certificate " + nifiCert + " and key " + nifiKey);
+            }
+        } else if (nifiKey.exists()) {
+            throw new IOException(nifiKey + " exists already, but " + nifiCert + " does not, we need both certificate and key to continue with an existing CA.");
+        } else {
+            TlsCertificateAuthorityManager tlsCertificateAuthorityManager = new TlsCertificateAuthorityManager(standaloneConfig);
+            KeyStore.PrivateKeyEntry privateKeyEntry = tlsCertificateAuthorityManager.getOrGenerateCertificateAuthority();
+            certificate = (X509Certificate) privateKeyEntry.getCertificateChain()[0];
+            caKeyPair = new KeyPair(certificate.getPublicKey(), privateKeyEntry.getPrivateKey());
+
+            try (PemWriter pemWriter = new PemWriter(new OutputStreamWriter(outputStreamFactory.create(nifiCert)))) {
+                pemWriter.writeObject(new JcaMiscPEMGenerator(certificate));
+            }
 
-        try (PemWriter pemWriter = new PemWriter(new OutputStreamWriter(outputStreamFactory.create(nifiKey)))) {
-            pemWriter.writeObject(new JcaMiscPEMGenerator(caKeyPair));
+            try (PemWriter pemWriter = new PemWriter(new OutputStreamWriter(outputStreamFactory.create(nifiKey)))) {
+                pemWriter.writeObject(new JcaMiscPEMGenerator(caKeyPair));
+            }
+
+            if (logger.isInfoEnabled()) {
+                logger.info("Generated new CA certificate " + nifiCert + " and key " + nifiKey);
+            }
         }
 
+        List<String> keyStorePasswords = standaloneConfig.getKeyStorePasswords();
+        List<String> keyPasswords = standaloneConfig.getKeyPasswords();
+        List<String> trustStorePasswords = standaloneConfig.getTrustStorePasswords();
+        NiFiPropertiesWriterFactory niFiPropertiesWriterFactory = standaloneConfig.getNiFiPropertiesWriterFactory();
+        int httpsPort = standaloneConfig.getHttpsPort();
+        boolean overwrite = standaloneConfig.isOverwrite();
+
+        if (hostnames.isEmpty() && logger.isInfoEnabled()) {
+            logger.info("No " + TlsToolkitStandaloneCommandLine.HOSTNAMES_ARG + " specified, not generating any host certificates or configuration.");
+        }
         for (int i = 0; i < hostnames.size(); i++) {
             String hostname = hostnames.get(i);
             File hostDir = new File(baseDir, hostname);
 
-            if (!hostDir.mkdirs()) {
+            TlsClientConfig tlsClientConfig = new TlsClientConfig(standaloneConfig);
+            File keystore = new File(hostDir, "keystore." + tlsClientConfig.getKeyStoreType().toLowerCase());
+            File truststore = new File(hostDir, "truststore." + tlsClientConfig.getTrustStoreType().toLowerCase());
+
+            if (hostDir.exists()) {
+                if (!hostDir.isDirectory()) {
+                    throw new IOException(hostDir + " exists but is not a directory.");
+                } else if (overwrite) {
+                    if (logger.isInfoEnabled()) {
+                        logger.info("Overwriting any existing ssl configuration in " + hostDir);
+                    }
+                    keystore.delete();
+                    if (keystore.exists()) {
+                        throw new IOException("Keystore " + keystore + " already exists and couldn't be deleted.");
+                    }
+                    truststore.delete();
+                    if (truststore.exists()) {
+                        throw new IOException("Truststore " + truststore + " already exists and couldn't be deleted.");
+                    }
+                } else {
+                    throw new IOException(hostDir + " exists and overwrite is not set.");
+                }
+            } else if (!hostDir.mkdirs()) {
                 throw new IOException("Unable to make directory: " + hostDir.getAbsolutePath());
+            } else if (logger.isInfoEnabled()) {
+                logger.info("Writing new ssl configuration to " + hostDir);
             }
 
-            TlsClientConfig tlsClientConfig = new TlsClientConfig(tlsConfig);
-            tlsClientConfig.setKeyStore(new File(hostDir, "keystore." + tlsClientConfig.getKeyStoreType().toLowerCase()).getAbsolutePath());
+            tlsClientConfig.setKeyStore(keystore.getAbsolutePath());
             tlsClientConfig.setKeyStorePassword(keyStorePasswords.get(i));
             tlsClientConfig.setKeyPassword(keyPasswords.get(i));
-            tlsClientConfig.setTrustStore(new File(hostDir, "truststore." + tlsClientConfig.getTrustStoreType().toLowerCase()).getAbsolutePath());
+            tlsClientConfig.setTrustStore(truststore.getAbsolutePath());
             tlsClientConfig.setTrustStorePassword(trustStorePasswords.get(i));
             TlsClientManager tlsClientManager = new TlsClientManager(tlsClientConfig);
             KeyPair keyPair = TlsHelper.generateKeyPair(keyPairAlgorithm, keySize);
@@ -132,8 +188,50 @@ public class TlsToolkitStandalone {
             }
         }
 
+        List<String> clientDns = standaloneConfig.getClientDns();
+        if (standaloneConfig.getClientDns().isEmpty() && logger.isInfoEnabled()) {
+            logger.info("No " + TlsToolkitStandaloneCommandLine.CLIENT_CERT_DN_ARG + " specified, not generating any client certificates.");
+        }
+
+        List<String> clientPasswords = standaloneConfig.getClientPasswords();
+        for (int i = 0; i < clientDns.size(); i++) {
+            String reorderedDn = CertificateUtils.reorderDn(clientDns.get(i));
+            String clientDnFile = getClientDnFile(reorderedDn);
+            File clientCertFile = new File(baseDir, clientDnFile + ".p12");
+
+            if (clientCertFile.exists()) {
+                if (overwrite) {
+                    if (logger.isInfoEnabled()) {
+                        logger.info("Overwriting existing client cert " + clientCertFile);
+                    }
+                } else {
+                    throw new IOException(clientCertFile + " exists and overwrite is not set.");
+                }
+            } else if (logger.isInfoEnabled()) {
+                logger.info("Generating new client certificate " + clientCertFile);
+            }
+            KeyPair keyPair = TlsHelper.generateKeyPair(keyPairAlgorithm, keySize);
+            X509Certificate clientCert = CertificateUtils.generateIssuedCertificate(reorderedDn, keyPair.getPublic(), certificate, caKeyPair, signingAlgorithm, days);
+            KeyStore keyStore = KeyStore.getInstance(BaseTlsManager.PKCS_12, BouncyCastleProvider.PROVIDER_NAME);
+            keyStore.load(null, null);
+            keyStore.setKeyEntry(NIFI_KEY, keyPair.getPrivate(), null, new Certificate[]{clientCert, certificate});
+            String password = TlsHelper.writeKeyStore(keyStore, outputStreamFactory, clientCertFile, clientPasswords.get(i), standaloneConfig.isClientPasswordsGenerated());
+
+            try (FileWriter fileWriter = new FileWriter(new File(baseDir, clientDnFile + ".password"))) {
+                fileWriter.write(password);
+            }
+
+            if (logger.isInfoEnabled()) {
+                logger.info("Successfully generated client certificate " + clientCertFile);
+            }
+        }
+
         if (logger.isInfoEnabled()) {
-            logger.info("Successfully generated TLS configuration for all hosts");
+            logger.info("tls-toolkit standalone completed successfully");
         }
     }
+
+    protected static String getClientDnFile(String clientDn) {
+        return clientDn.replace(',', '_').replace(' ', '_');
+    }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/fa5da543/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneCommandLine.java
----------------------------------------------------------------------
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 697148f..602fce4 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
@@ -21,7 +21,7 @@ import org.apache.commons.cli.CommandLine;
 import org.apache.nifi.toolkit.tls.commandLine.BaseCommandLine;
 import org.apache.nifi.toolkit.tls.commandLine.CommandLineParseException;
 import org.apache.nifi.toolkit.tls.commandLine.ExitCode;
-import org.apache.nifi.toolkit.tls.configuration.TlsConfig;
+import org.apache.nifi.toolkit.tls.configuration.StandaloneConfig;
 import org.apache.nifi.toolkit.tls.properties.NiFiPropertiesWriterFactory;
 import org.apache.nifi.toolkit.tls.util.PasswordUtil;
 import org.apache.nifi.toolkit.tls.util.TlsHelper;
@@ -48,9 +48,12 @@ public class TlsToolkitStandaloneCommandLine extends BaseCommandLine {
     public static final String KEY_PASSWORD_ARG = "keyPassword";
     public static final String HOSTNAMES_ARG = "hostnames";
     public static final String HTTPS_PORT_ARG = "httpsPort";
+    public static final String OVERWRITE_ARG = "isOverwrite";
+    public static final String CLIENT_CERT_DN_ARG = "clientCertDn";
+    public static final String CLIENT_CERT_PASSWORD_ARG = "clientCertPassword";
 
     public static final String DEFAULT_OUTPUT_DIRECTORY = "../" + Paths.get(".").toAbsolutePath().normalize().getFileName().toString();
-    public static final int DEFAULT_HTTPS_PORT = 10443;
+    public static final int DEFAULT_HTTPS_PORT = 9091;
 
     public static final String DESCRIPTION = "Creates certificates and config files for nifi cluster.";
 
@@ -64,6 +67,10 @@ public class TlsToolkitStandaloneCommandLine extends BaseCommandLine {
     private List<String> keyStorePasswords;
     private List<String> keyPasswords;
     private List<String> trustStorePasswords;
+    private List<String> clientDns;
+    private List<String> clientPasswords;
+    private boolean clientPasswordsGenerated;
+    private boolean overwrite;
 
     public TlsToolkitStandaloneCommandLine() {
         this(new PasswordUtil());
@@ -73,12 +80,15 @@ public class TlsToolkitStandaloneCommandLine extends BaseCommandLine {
         super(DESCRIPTION);
         this.passwordUtil = passwordUtil;
         addOptionWithArg("o", OUTPUT_DIRECTORY_ARG, "The directory to output keystores, truststore, config files.", DEFAULT_OUTPUT_DIRECTORY);
-        addOptionWithArg("n", HOSTNAMES_ARG, "Comma separated list of hostnames.", TlsConfig.DEFAULT_HOSTNAME);
+        addOptionWithArg("n", HOSTNAMES_ARG, "Comma separated list of hostnames.");
         addOptionWithArg("p", HTTPS_PORT_ARG, "Https port to use.", DEFAULT_HTTPS_PORT);
         addOptionWithArg("f", NIFI_PROPERTIES_FILE_ARG, "Base nifi.properties file to update. (Embedded file identical to the one in a default NiFi install will be used if not specified.)");
         addOptionWithArg("S", KEY_STORE_PASSWORD_ARG, "Keystore password to use.  Must either be one value or one for each host. (autogenerate if not specified)");
         addOptionWithArg("K", KEY_PASSWORD_ARG, "Key password to use.  Must either be one value or one for each host. (autogenerate if not specified)");
         addOptionWithArg("P", TRUST_STORE_PASSWORD_ARG, "Keystore password to use.  Must either be one value or one for each host. (autogenerate if not specified)");
+        addOptionWithArg("C", CLIENT_CERT_DN_ARG, "Generate client certificate suitable for use in browser with specified DN. (Can be specified multiple times.)");
+        addOptionWithArg("B", CLIENT_CERT_PASSWORD_ARG, "Password for client certificate.  Must either be one value or one for each client DN. (autogenerate if not specified)");
+        addOptionNoArg("O", OVERWRITE_ARG, "Overwrite existing host output.");
     }
 
     public static void main(String[] args) {
@@ -90,9 +100,7 @@ public class TlsToolkitStandaloneCommandLine extends BaseCommandLine {
             System.exit(e.getExitCode());
         }
         try {
-            new TlsToolkitStandalone().createNifiKeystoresAndTrustStores(tlsToolkitStandaloneCommandLine.getBaseDir(), tlsToolkitStandaloneCommandLine.createConfig(),
-                    tlsToolkitStandaloneCommandLine.getNiFiPropertiesWriterFactory(), tlsToolkitStandaloneCommandLine.getHostnames(), tlsToolkitStandaloneCommandLine.getKeyStorePasswords(),
-                    tlsToolkitStandaloneCommandLine.getKeyPasswords(), tlsToolkitStandaloneCommandLine.getTrustStorePasswords(), tlsToolkitStandaloneCommandLine.getHttpsPort());
+            new TlsToolkitStandalone().createNifiKeystoresAndTrustStores(tlsToolkitStandaloneCommandLine.createConfig());
         } catch (Exception e) {
             tlsToolkitStandaloneCommandLine.printUsage("Error creating generating tls configuration. (" + e.getMessage() + ")");
             System.exit(ExitCode.ERROR_GENERATING_CONFIG.ordinal());
@@ -105,13 +113,29 @@ public class TlsToolkitStandaloneCommandLine extends BaseCommandLine {
         CommandLine commandLine = super.doParse(args);
         String outputDirectory = commandLine.getOptionValue(OUTPUT_DIRECTORY_ARG, DEFAULT_OUTPUT_DIRECTORY);
         baseDir = new File(outputDirectory);
-        hostnames = Arrays.stream(commandLine.getOptionValue(HOSTNAMES_ARG, TlsConfig.DEFAULT_HOSTNAME).split(",")).map(String::trim).collect(Collectors.toList());
+
+        if (commandLine.hasOption(HOSTNAMES_ARG)) {
+            hostnames = Collections.unmodifiableList(Arrays.stream(commandLine.getOptionValue(HOSTNAMES_ARG).split(",")).map(String::trim).collect(Collectors.toList()));
+        } else {
+            hostnames = Collections.emptyList();
+        }
+
+        String[] clientDnValues = commandLine.getOptionValues(CLIENT_CERT_DN_ARG);
+        if (clientDnValues != null) {
+            clientDns = Collections.unmodifiableList(Arrays.stream(clientDnValues).collect(Collectors.toList()));
+        } else {
+            clientDns = Collections.emptyList();
+        }
+
         httpsPort = getIntValue(commandLine, HTTPS_PORT_ARG, DEFAULT_HTTPS_PORT);
 
         int numHosts = hostnames.size();
-        keyStorePasswords = Collections.unmodifiableList(getPasswords(KEY_STORE_PASSWORD_ARG, commandLine, numHosts));
+        keyStorePasswords = Collections.unmodifiableList(getPasswords(KEY_STORE_PASSWORD_ARG, commandLine, numHosts, HOSTNAMES_ARG));
         keyPasswords = Collections.unmodifiableList(getKeyPasswords(commandLine, keyStorePasswords));
-        trustStorePasswords = Collections.unmodifiableList(getPasswords(TRUST_STORE_PASSWORD_ARG, commandLine, numHosts));
+        trustStorePasswords = Collections.unmodifiableList(getPasswords(TRUST_STORE_PASSWORD_ARG, commandLine, numHosts, HOSTNAMES_ARG));
+        clientPasswords = Collections.unmodifiableList(getPasswords(CLIENT_CERT_PASSWORD_ARG, commandLine, clientDns.size(), CLIENT_CERT_DN_ARG));
+        clientPasswordsGenerated = commandLine.getOptionValues(CLIENT_CERT_PASSWORD_ARG) == null;
+        overwrite = commandLine.hasOption(OVERWRITE_ARG);
 
         String nifiPropertiesFile = commandLine.getOptionValue(NIFI_PROPERTIES_FILE_ARG, "");
         try {
@@ -128,64 +152,50 @@ public class TlsToolkitStandaloneCommandLine extends BaseCommandLine {
         return commandLine;
     }
 
-    private List<String> getPasswords(String arg, CommandLine commandLine, int numHosts) throws CommandLineParseException {
+    private List<String> getPasswords(String arg, CommandLine commandLine, int num, String numArg) throws CommandLineParseException {
         String[] optionValues = commandLine.getOptionValues(arg);
         if (optionValues == null) {
-            return IntStream.range(0, numHosts).mapToObj(operand -> passwordUtil.generatePassword()).collect(Collectors.toList());
+            return IntStream.range(0, num).mapToObj(operand -> passwordUtil.generatePassword()).collect(Collectors.toList());
         }
         if (optionValues.length == 1) {
-            return IntStream.range(0, numHosts).mapToObj(value -> optionValues[0]).collect(Collectors.toList());
-        } else if (optionValues.length == numHosts) {
+            return IntStream.range(0, num).mapToObj(value -> optionValues[0]).collect(Collectors.toList());
+        } else if (optionValues.length == num) {
             return Arrays.stream(optionValues).collect(Collectors.toList());
         }
-        return printUsageAndThrow("Expected either 1 value or " + numHosts + " (the number of hostnames) values for " + arg, ExitCode.ERROR_INCORRECT_NUMBER_OF_PASSWORDS);
+        return printUsageAndThrow("Expected either 1 value or " + num + " (the number of " + numArg + ") values for " + arg, ExitCode.ERROR_INCORRECT_NUMBER_OF_PASSWORDS);
     }
 
     private List<String> getKeyPasswords(CommandLine commandLine, List<String> keyStorePasswords) throws CommandLineParseException {
         if (differentPasswordForKeyAndKeystore() || commandLine.hasOption(KEY_PASSWORD_ARG)) {
-            return getPasswords(KEY_PASSWORD_ARG, commandLine, keyStorePasswords.size());
+            return getPasswords(KEY_PASSWORD_ARG, commandLine, keyStorePasswords.size(), HOSTNAMES_ARG);
         }
         return new ArrayList<>(keyStorePasswords);
     }
 
-    public File getBaseDir() {
-        return baseDir;
-    }
-
-    public List<String> getHostnames() {
-        return hostnames;
-    }
-
-    public int getHttpsPort() {
-        return httpsPort;
-    }
-
-    public NiFiPropertiesWriterFactory getNiFiPropertiesWriterFactory() {
-        return niFiPropertiesWriterFactory;
-    }
-
-    public List<String> getKeyStorePasswords() {
-        return keyStorePasswords;
-    }
-
-    public List<String> getKeyPasswords() {
-        return keyPasswords;
-    }
-
-    public List<String> getTrustStorePasswords() {
-        return trustStorePasswords;
-    }
-
-    public TlsConfig createConfig() throws IOException {
-        TlsConfig tlsConfig = new TlsConfig();
-        tlsConfig.setCaHostname(getCertificateAuthorityHostname());
-        tlsConfig.setKeyStore("nifi-ca-" + KEYSTORE + getKeyStoreType().toLowerCase());
-        tlsConfig.setKeyStoreType(getKeyStoreType());
-        tlsConfig.setKeySize(getKeySize());
-        tlsConfig.setKeyPairAlgorithm(getKeyAlgorithm());
-        tlsConfig.setSigningAlgorithm(getSigningAlgorithm());
-        tlsConfig.setDays(getDays());
-        tlsConfig.initDefaults();
-        return tlsConfig;
+    public StandaloneConfig createConfig() {
+        StandaloneConfig standaloneConfig = new StandaloneConfig();
+
+        standaloneConfig.setBaseDir(baseDir);
+        standaloneConfig.setNiFiPropertiesWriterFactory(niFiPropertiesWriterFactory);
+        standaloneConfig.setHostnames(hostnames);
+        standaloneConfig.setKeyStorePasswords(keyStorePasswords);
+        standaloneConfig.setKeyPasswords(keyPasswords);
+        standaloneConfig.setTrustStorePasswords(trustStorePasswords);
+        standaloneConfig.setHttpsPort(httpsPort);
+        standaloneConfig.setOverwrite(overwrite);
+        standaloneConfig.setClientDns(clientDns);
+        standaloneConfig.setClientPasswords(clientPasswords);
+        standaloneConfig.setClientPasswordsGenerated(clientPasswordsGenerated);
+
+        standaloneConfig.setCaHostname(getCertificateAuthorityHostname());
+        standaloneConfig.setKeyStore("nifi-ca-" + KEYSTORE + getKeyStoreType().toLowerCase());
+        standaloneConfig.setKeyStoreType(getKeyStoreType());
+        standaloneConfig.setKeySize(getKeySize());
+        standaloneConfig.setKeyPairAlgorithm(getKeyAlgorithm());
+        standaloneConfig.setSigningAlgorithm(getSigningAlgorithm());
+        standaloneConfig.setDays(getDays());
+        standaloneConfig.initDefaults();
+
+        return standaloneConfig;
     }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/fa5da543/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/util/TlsHelper.java
----------------------------------------------------------------------
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 0622b9e..05a5dcb 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,29 +17,38 @@
 
 package org.apache.nifi.toolkit.tls.util;
 
+import org.bouncycastle.asn1.x500.X500Name;
 import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
 import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
+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.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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
+import javax.crypto.Cipher;
 import javax.crypto.Mac;
 import javax.crypto.spec.SecretKeySpec;
-import javax.security.auth.x500.X500Principal;
+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.Security;
@@ -47,10 +56,78 @@ import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 
 public class TlsHelper {
+    private static final Logger logger = LoggerFactory.getLogger(TlsHelper.class);
+    private static final int DEFAULT_MAX_ALLOWED_KEY_LENGTH = 128;
+    public static final String JCE_URL = "http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html";
+    public static final String ILLEGAL_KEY_SIZE = "illegal key size";
+    private static boolean isUnlimitedStrengthCryptographyEnabled;
+
+    // Evaluate an unlimited strength algorithm to determine if we support the capability we have on the system
+    static {
+        try {
+            isUnlimitedStrengthCryptographyEnabled = (Cipher.getMaxAllowedKeyLength("AES") > DEFAULT_MAX_ALLOWED_KEY_LENGTH);
+        } catch (NoSuchAlgorithmException e) {
+            // if there are issues with this, we default back to the value established
+            isUnlimitedStrengthCryptographyEnabled = false;
+        }
+    }
+
+    private static void logTruncationWarning(File file) {
+        String fileToString = file.toString();
+        String fileName = file.getName();
+        logger.warn("**********************************************************************************");
+        logger.warn("                                    WARNING!!!!");
+        logger.warn("**********************************************************************************");
+        logger.warn("Unlimited JCE Policy is not installed which means we cannot utilize a");
+        logger.warn("PKCS12 password longer than 7 characters.");
+        logger.warn("Autogenerated password has been reduced to 7 characters.");
+        logger.warn("");
+        logger.warn("Please strongly consider installing Unlimited JCE Policy at");
+        logger.warn(JCE_URL);
+        logger.warn("");
+        logger.warn("Another alternative is to add a stronger password with the openssl tool to the");
+        logger.warn("resulting client certificate: " + fileToString);
+        logger.warn("");
+        logger.warn("openssl pkcs12 -in '" + fileToString + "' -out '/tmp/" + fileName + "'");
+        logger.warn("openssl pkcs12 -export -in '/tmp/"  + fileName + "' -out '" + fileToString + "'");
+        logger.warn("rm -f '/tmp/" + fileName + "'");
+        logger.warn("");
+        logger.warn("**********************************************************************************");
+
+    }
+
     private TlsHelper() {
 
     }
 
+    public static boolean isUnlimitedStrengthCryptographyEnabled() {
+        return isUnlimitedStrengthCryptographyEnabled;
+    }
+
+    public static String writeKeyStore(KeyStore keyStore, OutputStreamFactory outputStreamFactory, File file, String password, boolean generatedPassword) throws IOException, GeneralSecurityException {
+        try (OutputStream fileOutputStream = outputStreamFactory.create(file)) {
+            keyStore.store(fileOutputStream, password.toCharArray());
+        } catch (IOException e) {
+            if (e.getMessage().toLowerCase().contains(ILLEGAL_KEY_SIZE) && !isUnlimitedStrengthCryptographyEnabled()) {
+                if (generatedPassword) {
+                    file.delete();
+                    String truncatedPassword = password.substring(0, 7);
+                    try (OutputStream fileOutputStream = outputStreamFactory.create(file)) {
+                        keyStore.store(fileOutputStream, truncatedPassword.toCharArray());
+                    }
+                    logTruncationWarning(file);
+                    return truncatedPassword;
+                } else {
+                    throw new GeneralSecurityException("Specified password for " + file + " too long to work without unlimited JCE policy installed."
+                            + System.lineSeparator() + "Please see " + JCE_URL);
+                }
+            } else {
+                throw e;
+            }
+        }
+        return password;
+    }
+
     public static void addBouncyCastleProvider() {
         Security.addProvider(new BouncyCastleProvider());
     }
@@ -90,13 +167,21 @@ public class TlsHelper {
         }
     }
 
-    public static X509Certificate parseCertificate(String pemEncodedCertificate) throws IOException, CertificateException {
-        try (PEMParser pemParser = new PEMParser(new StringReader(pemEncodedCertificate))) {
+    public static X509Certificate parseCertificate(Reader pemEncodedCertificate) throws IOException, CertificateException {
+        return new JcaX509CertificateConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME).getCertificate(parsePem(X509CertificateHolder.class, pemEncodedCertificate));
+    }
+
+    public static KeyPair parseKeyPair(Reader pemEncodedKeyPair) throws IOException {
+        return new JcaPEMKeyConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME).getKeyPair(parsePem(PEMKeyPair.class, pemEncodedKeyPair));
+    }
+
+    public static <T> T parsePem(Class<T> clazz, Reader pemReader) throws IOException {
+        try (PEMParser pemParser = new PEMParser(pemReader)) {
             Object object = pemParser.readObject();
-            if (!X509CertificateHolder.class.isInstance(object)) {
-                throw new IOException("Expected " + X509CertificateHolder.class);
+            if (!clazz.isInstance(object)) {
+                throw new IOException("Expected " + clazz);
             }
-            return new JcaX509CertificateConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME).getCertificate((X509CertificateHolder) object);
+            return (T) object;
         }
     }
 
@@ -105,7 +190,7 @@ public class TlsHelper {
     }
 
     public static JcaPKCS10CertificationRequest generateCertificationRequest(String requestedDn, KeyPair keyPair, String signingAlgorithm) throws OperatorCreationException {
-        JcaPKCS10CertificationRequestBuilder jcaPKCS10CertificationRequestBuilder = new JcaPKCS10CertificationRequestBuilder(new X500Principal(requestedDn), keyPair.getPublic());
+        JcaPKCS10CertificationRequestBuilder jcaPKCS10CertificationRequestBuilder = new JcaPKCS10CertificationRequestBuilder(new X500Name(requestedDn), keyPair.getPublic());
         JcaContentSignerBuilder jcaContentSignerBuilder = new JcaContentSignerBuilder(signingAlgorithm);
         return new JcaPKCS10CertificationRequest(jcaPKCS10CertificationRequestBuilder.build(jcaContentSignerBuilder.build(keyPair.getPrivate())));
     }

http://git-wip-us.apache.org/repos/asf/nifi/blob/fa5da543/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/service/TlsCertificateAuthorityTest.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/service/TlsCertificateAuthorityTest.java b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/service/TlsCertificateAuthorityTest.java
index 7881500..ad5fa12 100644
--- a/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/service/TlsCertificateAuthorityTest.java
+++ b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/service/TlsCertificateAuthorityTest.java
@@ -107,7 +107,7 @@ public class TlsCertificateAuthorityTest {
 
         clientConfig = new TlsClientConfig();
         clientConfig.setCaHostname("localhost");
-        clientConfig.setDn("CN=otherHostname,OU=NIFI");
+        clientConfig.setDn("OU=NIFI,CN=otherHostname");
         clientConfig.setKeyStore(clientKeyStore);
         clientConfig.setTrustStore(clientTrustStore);
         clientConfig.setToken(myTestTokenUseSomethingStronger);
@@ -227,7 +227,7 @@ public class TlsCertificateAuthorityTest {
         assertEquals(caCertificate, clientTrustStore.getCertificate(TlsToolkitStandalone.NIFI_CERT));
     }
 
-    private void assertPrivateAndPublicKeyMatch(PrivateKey privateKey, PublicKey publicKey) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
+    public static void assertPrivateAndPublicKeyMatch(PrivateKey privateKey, PublicKey publicKey) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
         Signature signature = Signature.getInstance(TlsConfig.DEFAULT_SIGNING_ALGORITHM);
         signature.initSign(privateKey);
         byte[] bytes = "test string".getBytes(StandardCharsets.UTF_8);

http://git-wip-us.apache.org/repos/asf/nifi/blob/fa5da543/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/service/server/TlsCertificateAuthorityServiceHandlerTest.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/service/server/TlsCertificateAuthorityServiceHandlerTest.java b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/service/server/TlsCertificateAuthorityServiceHandlerTest.java
index bf355fa..09bc8f0 100644
--- a/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/service/server/TlsCertificateAuthorityServiceHandlerTest.java
+++ b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/service/server/TlsCertificateAuthorityServiceHandlerTest.java
@@ -145,7 +145,7 @@ public class TlsCertificateAuthorityServiceHandlerTest {
         tlsCertificateAuthorityServiceHandler.handle(null, baseRequest, httpServletRequest, httpServletResponse);
         assertEquals(Response.SC_OK, statusCode);
         assertArrayEquals(testCaHmac, getResponse().getHmac());
-        X509Certificate certificate = TlsHelper.parseCertificate(getResponse().getPemEncodedCertificate());
+        X509Certificate certificate = TlsHelper.parseCertificate(new StringReader(getResponse().getPemEncodedCertificate()));
         assertEquals(certificateKeyPair.getPublic(), certificate.getPublicKey());
         assertEquals(new X500Name(requestedDn), new X500Name(certificate.getSubjectDN().toString()));
         certificate.verify(caCert.getPublicKey());

http://git-wip-us.apache.org/repos/asf/nifi/blob/fa5da543/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneCommandLineTest.java
----------------------------------------------------------------------
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 fd45eb4..82c67d9 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
@@ -19,6 +19,8 @@ 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.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;
@@ -31,6 +33,7 @@ import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
 import java.security.SecureRandom;
+import java.util.Collections;
 import java.util.List;
 import java.util.Properties;
 import java.util.Random;
@@ -75,7 +78,7 @@ public class TlsToolkitStandaloneCommandLineTest {
     }
 
     @Test
-    public void testKeyAlgorithm() throws CommandLineParseException, IOException {
+    public void testKeyAlgorithm() throws CommandLineParseException {
         String testKeyAlgorithm = "testKeyAlgorithm";
         tlsToolkitStandaloneCommandLine.parse("-a", testKeyAlgorithm);
         assertEquals(testKeyAlgorithm, tlsToolkitStandaloneCommandLine.createConfig().getKeyPairAlgorithm());
@@ -115,7 +118,7 @@ public class TlsToolkitStandaloneCommandLineTest {
     }
 
     @Test
-    public void testDays() throws CommandLineParseException, IOException {
+    public void testDays() throws CommandLineParseException {
         int testDays = 29;
         tlsToolkitStandaloneCommandLine.parse("-d", Integer.toString(testDays));
         assertEquals(testDays, tlsToolkitStandaloneCommandLine.createConfig().getDays());
@@ -132,7 +135,7 @@ public class TlsToolkitStandaloneCommandLineTest {
     public void testOutputDirectory() throws CommandLineParseException {
         String testPath = File.separator + "fake" + File.separator + "path" + File.separator + "doesnt" + File.separator + "exist";
         tlsToolkitStandaloneCommandLine.parse("-o", testPath);
-        assertEquals(testPath, tlsToolkitStandaloneCommandLine.getBaseDir().getPath());
+        assertEquals(testPath, tlsToolkitStandaloneCommandLine.createConfig().getBaseDir().getPath());
     }
 
     @Test
@@ -142,7 +145,7 @@ public class TlsToolkitStandaloneCommandLineTest {
 
         tlsToolkitStandaloneCommandLine.parse("-n", nifi1 + " , " + nifi2);
 
-        List<String> hostnames = tlsToolkitStandaloneCommandLine.getHostnames();
+        List<String> hostnames = tlsToolkitStandaloneCommandLine.createConfig().getHostnames();
         assertEquals(2, hostnames.size());
         assertEquals(nifi1, hostnames.get(0));
         assertEquals(nifi2, hostnames.get(1));
@@ -152,7 +155,7 @@ public class TlsToolkitStandaloneCommandLineTest {
     public void testHttpsPort() throws CommandLineParseException {
         int testPort = 8998;
         tlsToolkitStandaloneCommandLine.parse("-p", Integer.toString(testPort));
-        assertEquals(testPort, tlsToolkitStandaloneCommandLine.getHttpsPort());
+        assertEquals(testPort, tlsToolkitStandaloneCommandLine.createConfig().getHttpsPort());
     }
 
     @Test
@@ -179,10 +182,10 @@ public class TlsToolkitStandaloneCommandLineTest {
 
     @Test
     public void testNotSameKeyAndKeystorePassword() throws CommandLineParseException {
-        tlsToolkitStandaloneCommandLine.parse("-g");
-        List<String> keyStorePasswords = tlsToolkitStandaloneCommandLine.getKeyStorePasswords();
-        List<String> keyPasswords = tlsToolkitStandaloneCommandLine.getKeyPasswords();
-        assertEquals(1, tlsToolkitStandaloneCommandLine.getHostnames().size());
+        tlsToolkitStandaloneCommandLine.parse("-g", "-n", TlsConfig.DEFAULT_HOSTNAME);
+        List<String> keyStorePasswords = tlsToolkitStandaloneCommandLine.createConfig().getKeyStorePasswords();
+        List<String> keyPasswords = tlsToolkitStandaloneCommandLine.createConfig().getKeyPasswords();
+        assertEquals(1, tlsToolkitStandaloneCommandLine.createConfig().getHostnames().size());
         assertEquals(1, keyStorePasswords.size());
         assertEquals(1, keyPasswords.size());
         assertNotEquals(keyStorePasswords.get(0), keyPasswords.get(0));
@@ -190,10 +193,10 @@ public class TlsToolkitStandaloneCommandLineTest {
 
     @Test
     public void testSameKeyAndKeystorePassword() throws CommandLineParseException {
-        tlsToolkitStandaloneCommandLine.parse();
-        List<String> keyStorePasswords = tlsToolkitStandaloneCommandLine.getKeyStorePasswords();
-        List<String> keyPasswords = tlsToolkitStandaloneCommandLine.getKeyPasswords();
-        assertEquals(1, tlsToolkitStandaloneCommandLine.getHostnames().size());
+        tlsToolkitStandaloneCommandLine.parse("-n", TlsConfig.DEFAULT_HOSTNAME);
+        List<String> keyStorePasswords = tlsToolkitStandaloneCommandLine.createConfig().getKeyStorePasswords();
+        List<String> keyPasswords = tlsToolkitStandaloneCommandLine.createConfig().getKeyPasswords();
+        assertEquals(1, tlsToolkitStandaloneCommandLine.createConfig().getHostnames().size());
         assertEquals(1, keyStorePasswords.size());
         assertEquals(1, keyPasswords.size());
         assertEquals(keyStorePasswords.get(0), keyPasswords.get(0));
@@ -202,19 +205,19 @@ public class TlsToolkitStandaloneCommandLineTest {
     @Test
     public void testSameKeyAndKeystorePasswordWithKeystorePasswordSpecified() throws CommandLineParseException {
         String testPassword = "testPassword";
-        tlsToolkitStandaloneCommandLine.parse("-S", testPassword);
-        List<String> keyStorePasswords = tlsToolkitStandaloneCommandLine.getKeyStorePasswords();
+        tlsToolkitStandaloneCommandLine.parse("-S", testPassword, "-n", TlsConfig.DEFAULT_HOSTNAME);
+        List<String> keyStorePasswords = tlsToolkitStandaloneCommandLine.createConfig().getKeyStorePasswords();
         assertEquals(1, keyStorePasswords.size());
         assertEquals(testPassword, keyStorePasswords.get(0));
-        assertEquals(keyStorePasswords, tlsToolkitStandaloneCommandLine.getKeyPasswords());
+        assertEquals(keyStorePasswords, tlsToolkitStandaloneCommandLine.createConfig().getKeyPasswords());
     }
 
     @Test
     public void testSameKeyAndKeystorePasswordWithKeyPasswordSpecified() throws CommandLineParseException {
         String testPassword = "testPassword";
-        tlsToolkitStandaloneCommandLine.parse("-K", testPassword);
-        List<String> keyPasswords = tlsToolkitStandaloneCommandLine.getKeyPasswords();
-        assertNotEquals(tlsToolkitStandaloneCommandLine.getKeyStorePasswords(), keyPasswords);
+        tlsToolkitStandaloneCommandLine.parse("-K", testPassword, "-n", TlsConfig.DEFAULT_HOSTNAME);
+        List<String> keyPasswords = tlsToolkitStandaloneCommandLine.createConfig().getKeyPasswords();
+        assertNotEquals(tlsToolkitStandaloneCommandLine.createConfig().getKeyStorePasswords(), keyPasswords);
         assertEquals(1, keyPasswords.size());
         assertEquals(testPassword, keyPasswords.get(0));
     }
@@ -222,8 +225,8 @@ public class TlsToolkitStandaloneCommandLineTest {
     @Test
     public void testKeyStorePasswordArg() throws CommandLineParseException {
         String testPassword = "testPassword";
-        tlsToolkitStandaloneCommandLine.parse("-S", testPassword);
-        List<String> keyStorePasswords = tlsToolkitStandaloneCommandLine.getKeyStorePasswords();
+        tlsToolkitStandaloneCommandLine.parse("-S", testPassword, "-n", TlsConfig.DEFAULT_HOSTNAME);
+        List<String> keyStorePasswords = tlsToolkitStandaloneCommandLine.createConfig().getKeyStorePasswords();
         assertEquals(1, keyStorePasswords.size());
         assertEquals(testPassword, keyStorePasswords.get(0));
     }
@@ -233,7 +236,7 @@ public class TlsToolkitStandaloneCommandLineTest {
         String testPassword1 = "testPassword1";
         String testPassword2 = "testPassword2";
         tlsToolkitStandaloneCommandLine.parse("-n", "nifi1,nifi2", "-S", testPassword1, "-S", testPassword2);
-        List<String> keyStorePasswords = tlsToolkitStandaloneCommandLine.getKeyStorePasswords();
+        List<String> keyStorePasswords = tlsToolkitStandaloneCommandLine.createConfig().getKeyStorePasswords();
         assertEquals(2, keyStorePasswords.size());
         assertEquals(testPassword1, keyStorePasswords.get(0));
         assertEquals(testPassword2, keyStorePasswords.get(1));
@@ -254,8 +257,8 @@ public class TlsToolkitStandaloneCommandLineTest {
     @Test
     public void testKeyPasswordArg() throws CommandLineParseException {
         String testPassword = "testPassword";
-        tlsToolkitStandaloneCommandLine.parse("-K", testPassword);
-        List<String> keyPasswords = tlsToolkitStandaloneCommandLine.getKeyPasswords();
+        tlsToolkitStandaloneCommandLine.parse("-K", testPassword, "-n", TlsConfig.DEFAULT_HOSTNAME);
+        List<String> keyPasswords = tlsToolkitStandaloneCommandLine.createConfig().getKeyPasswords();
         assertEquals(1, keyPasswords.size());
         assertEquals(testPassword, keyPasswords.get(0));
     }
@@ -265,7 +268,7 @@ public class TlsToolkitStandaloneCommandLineTest {
         String testPassword1 = "testPassword1";
         String testPassword2 = "testPassword2";
         tlsToolkitStandaloneCommandLine.parse("-n", "nifi1,nifi2", "-K", testPassword1, "-K", testPassword2);
-        List<String> keyPasswords = tlsToolkitStandaloneCommandLine.getKeyPasswords();
+        List<String> keyPasswords = tlsToolkitStandaloneCommandLine.createConfig().getKeyPasswords();
         assertEquals(2, keyPasswords.size());
         assertEquals(testPassword1, keyPasswords.get(0));
         assertEquals(testPassword2, keyPasswords.get(1));
@@ -286,8 +289,8 @@ public class TlsToolkitStandaloneCommandLineTest {
     @Test
     public void testTruststorePasswordArg() throws CommandLineParseException {
         String testPassword = "testPassword";
-        tlsToolkitStandaloneCommandLine.parse("-P", testPassword);
-        List<String> trustStorePasswords = tlsToolkitStandaloneCommandLine.getTrustStorePasswords();
+        tlsToolkitStandaloneCommandLine.parse("-P", testPassword, "-n", TlsConfig.DEFAULT_HOSTNAME);
+        List<String> trustStorePasswords = tlsToolkitStandaloneCommandLine.createConfig().getTrustStorePasswords();
         assertEquals(1, trustStorePasswords.size());
         assertEquals(testPassword, trustStorePasswords.get(0));
     }
@@ -297,7 +300,7 @@ public class TlsToolkitStandaloneCommandLineTest {
         String testPassword1 = "testPassword1";
         String testPassword2 = "testPassword2";
         tlsToolkitStandaloneCommandLine.parse("-n", "nifi1,nifi2", "-P", testPassword1, "-P", testPassword2);
-        List<String> trustStorePasswords = tlsToolkitStandaloneCommandLine.getTrustStorePasswords();
+        List<String> trustStorePasswords = tlsToolkitStandaloneCommandLine.createConfig().getTrustStorePasswords();
         assertEquals(2, trustStorePasswords.size());
         assertEquals(testPassword1, trustStorePasswords.get(0));
         assertEquals(testPassword2, trustStorePasswords.get(1));
@@ -315,8 +318,54 @@ public class TlsToolkitStandaloneCommandLineTest {
         }
     }
 
+    @Test
+    public void testClientDnDefault() throws CommandLineParseException {
+        tlsToolkitStandaloneCommandLine.parse();
+        assertEquals(Collections.emptyList(), tlsToolkitStandaloneCommandLine.createConfig().getClientDns());
+    }
+
+    @Test
+    public void testClientDnSingle() throws CommandLineParseException {
+        String testCn = "OU=NIFI,CN=testuser";
+        tlsToolkitStandaloneCommandLine.parse("-C", testCn);
+        List<String> clientDns = tlsToolkitStandaloneCommandLine.createConfig().getClientDns();
+        assertEquals(1, clientDns.size());
+        assertEquals(testCn, clientDns.get(0));
+    }
+
+    @Test
+    public void testClientDnMulti() throws CommandLineParseException {
+        String testCn = "OU=NIFI,CN=testuser";
+        String testCn2 = "OU=NIFI,CN=testuser2";
+        tlsToolkitStandaloneCommandLine.parse("-C", testCn, "-C", testCn2);
+        StandaloneConfig standaloneConfig = tlsToolkitStandaloneCommandLine.createConfig();
+        List<String> clientDns = standaloneConfig.getClientDns();
+        assertEquals(2, clientDns.size());
+        assertEquals(testCn, clientDns.get(0));
+        assertEquals(testCn2, clientDns.get(1));
+        assertEquals(2, standaloneConfig.getClientPasswords().size());
+    }
+
+    @Test
+    public void testClientPasswordMulti() throws CommandLineParseException {
+        String testCn = "OU=NIFI,CN=testuser";
+        String testCn2 = "OU=NIFI,CN=testuser2";
+        String testPass1 = "testPass1";
+        String testPass2 = "testPass2";
+        tlsToolkitStandaloneCommandLine.parse("-C", testCn, "-C", testCn2, "-B", testPass1, "-B", testPass2);
+        StandaloneConfig standaloneConfig = tlsToolkitStandaloneCommandLine.createConfig();
+        List<String> clientDns = standaloneConfig.getClientDns();
+        assertEquals(2, clientDns.size());
+        assertEquals(testCn, clientDns.get(0));
+        assertEquals(testCn2, clientDns.get(1));
+        List<String> clientPasswords = standaloneConfig.getClientPasswords();
+        assertEquals(2, clientPasswords.size());
+        assertEquals(testPass1, clientPasswords.get(0));
+        assertEquals(testPass2, clientPasswords.get(1));
+    }
+
     private Properties getProperties() throws IOException {
-        NiFiPropertiesWriter niFiPropertiesWriter = tlsToolkitStandaloneCommandLine.getNiFiPropertiesWriterFactory().create();
+        NiFiPropertiesWriter niFiPropertiesWriter = tlsToolkitStandaloneCommandLine.createConfig().getNiFiPropertiesWriterFactory().create();
         ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
         niFiPropertiesWriter.writeNiFiProperties(byteArrayOutputStream);
         Properties properties = new Properties();