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 <[email protected]>


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 <[email protected]>
Authored: Tue Aug 9 11:11:21 2016 -0400
Committer: Bryan Bende <[email protected]>
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();

Reply via email to