This is an automated email from the ASF dual-hosted git repository.

alopresto pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/master by this push:
     new 12e2102  NIFI-4247 Support ranges in `tls-toolkit` SAN cli option.
12e2102 is described below

commit 12e210277bc1489d480c1ae99bcfed9c32203ff6
Author: Troy Melhase <[email protected]>
AuthorDate: Thu May 9 18:04:05 2019 -0800

    NIFI-4247 Support ranges in `tls-toolkit` SAN cli option.
    
    This closes #3466.
    
    Signed-off-by: Andy LoPresto <[email protected]>
---
 nifi-docs/src/main/asciidoc/toolkit-guide.adoc     |   9 +-
 .../tls/configuration/InstanceIdentifier.java      |   4 +-
 .../nifi/toolkit/tls/configuration/TlsConfig.java  |   7 +-
 .../TlsCertificateAuthorityClientCommandLine.java  |  29 ++-
 .../TlsCertificateSigningRequestPerformer.java     |   4 +-
 .../tls/standalone/TlsToolkitStandalone.java       |  19 +-
 .../TlsToolkitStandaloneCommandLine.java           |  19 +-
 .../apache/nifi/toolkit/tls/util/TlsHelper.java    |  13 +-
 .../tls/configuration/InstanceIdentifierTest.java  |   2 +-
 .../TlsToolkitStandaloneCommandLineTest.java       |   2 +-
 .../tls/standalone/TlsToolkitStandaloneTest.java   | 232 +++++++++++++++++++++
 .../nifi/toolkit/tls/util/TlsHelperTest.java       |   6 +-
 12 files changed, 317 insertions(+), 29 deletions(-)

diff --git a/nifi-docs/src/main/asciidoc/toolkit-guide.adoc 
b/nifi-docs/src/main/asciidoc/toolkit-guide.adoc
index 304f79c..9572d62 100644
--- a/nifi-docs/src/main/asciidoc/toolkit-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/toolkit-guide.adoc
@@ -775,9 +775,9 @@ The following are available options:
 * `-T`,`--keyStoreType <arg>`                   The type of keystores to 
generate (default: `jks`)
 
 
-Hostname Patterns:
+"Hostname" and "Subject Alternative Name" Patterns:
 
-* Square brackets can be used in order to easily specify a range of hostnames. 
Example: `[01-20]`
+* Square brackets can be used in order to easily specify a range of hostnames 
or subject alternative names. Example: `[01-20]`
 * Parentheses can be used in order to specify that more than one NiFi instance 
will run on the given host(s). Example: `(5)`
 
 Examples:
@@ -797,6 +797,11 @@ Create 2 sets of keystore, truststore, _nifi.properties_ 
for 10 NiFi hostnames i
 bin/tls-toolkit.sh standalone -n 'nifi[01-10].subdomain[1-4].domain(2)' -C 
'CN=username,OU=NIFI'
 ----
 
+The same command with a range of subject alternate names:
+----
+bin/tls-toolkit.sh standalone -n 'nifi[01-10].subdomain[1-4].domain(2)' -C 
'CN=username,OU=NIFI' --subjectAlternativeNames 
'nifi[21-30].other[2-5].example.com(2)'
+----
+
 ==== Client/Server
 Client/Server mode relies on a long-running Certificate Authority (CA) to 
issue certificates. The CA can be stopped when you’re not bringing nodes online.
 
diff --git 
a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/InstanceIdentifier.java
 
b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/InstanceIdentifier.java
index 9699236..7b15636 100644
--- 
a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/InstanceIdentifier.java
+++ 
b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/InstanceIdentifier.java
@@ -106,7 +106,9 @@ public class InstanceIdentifier {
             int low = Integer.parseInt(split[0]);
             String padding = split[0].substring(0, split[0].length() - 
Integer.toString(low).length());
             int high = Integer.parseInt(split[1]);
-            return IntStream.range(low, high + 1).mapToObj(i -> {
+            IntStream intRange = IntStream.range(Math.min(low, high), 
Math.max(low, high) + 1)
+                .map(i -> high < low ? high - i + low : i);
+            return intRange.mapToObj(i -> {
                 String s = Integer.toString(i);
                 int length = s.length();
                 if (length >= baseLength) {
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 9332022..5e440a7 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 java.util.List;
 import org.apache.nifi.security.util.CertificateUtils;
 import org.apache.nifi.util.StringUtils;
 
@@ -41,7 +42,6 @@ public class TlsConfig {
     private String signingAlgorithm = DEFAULT_SIGNING_ALGORITHM;
 
     private String dn;
-    private String domainAlternativeNames;
     private String keyStore;
     private String keyStoreType = DEFAULT_KEY_STORE_TYPE;
     private String keyStorePassword;
@@ -53,6 +53,7 @@ public class TlsConfig {
     private String dnSuffix = DEFAULT_DN_SUFFIX;
     private boolean reorderDn = DEFAULT_REORDER_DN;
     private String additionalCACertificate = "";
+    private List<String> domainAlternativeNames;
 
     public String calcDefaultDn(String hostname) {
         String dn = dnPrefix + hostname + dnSuffix;
@@ -209,11 +210,11 @@ public class TlsConfig {
         }
     }
 
-    public String getDomainAlternativeNames() {
+    public List<String> getDomainAlternativeNames() {
         return domainAlternativeNames;
     }
 
-    public void setDomainAlternativeNames(String domainAlternativeNames) {
+    public void setDomainAlternativeNames(List<String> domainAlternativeNames) 
{
         this.domainAlternativeNames = domainAlternativeNames;
     }
 
diff --git 
a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/service/client/TlsCertificateAuthorityClientCommandLine.java
 
b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/service/client/TlsCertificateAuthorityClientCommandLine.java
index dde1ff7..194418a 100644
--- 
a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/service/client/TlsCertificateAuthorityClientCommandLine.java
+++ 
b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/service/client/TlsCertificateAuthorityClientCommandLine.java
@@ -24,9 +24,14 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
 import org.apache.commons.cli.CommandLine;
 import org.apache.nifi.toolkit.tls.commandLine.CommandLineParseException;
 import org.apache.nifi.toolkit.tls.commandLine.ExitCode;
+import org.apache.nifi.toolkit.tls.configuration.InstanceDefinition;
 import org.apache.nifi.toolkit.tls.configuration.TlsClientConfig;
 import org.apache.nifi.toolkit.tls.service.BaseCertificateAuthorityCommandLine;
 import org.apache.nifi.toolkit.tls.util.InputStreamFactory;
@@ -47,7 +52,7 @@ public class TlsCertificateAuthorityClientCommandLine extends 
BaseCertificateAut
     private final InputStreamFactory inputStreamFactory;
 
     private String certificateDirectory;
-    private String domainAlternativeNames;
+    private List<InstanceDefinition> domainAlternativeNames;
 
     public TlsCertificateAuthorityClientCommandLine() {
         this(FileInputStream::new);
@@ -112,7 +117,20 @@ public class TlsCertificateAuthorityClientCommandLine 
extends BaseCertificateAut
     protected CommandLine doParse(String[] args) throws 
CommandLineParseException {
         CommandLine commandLine = super.doParse(args);
         certificateDirectory = 
commandLine.getOptionValue(CERTIFICATE_DIRECTORY, 
DEFAULT_CERTIFICATE_DIRECTORY);
-        domainAlternativeNames = 
commandLine.getOptionValue(SUBJECT_ALTERNATIVE_NAMES);
+
+        if (commandLine.hasOption(SUBJECT_ALTERNATIVE_NAMES)) {
+            domainAlternativeNames = Collections.unmodifiableList(
+                    InstanceDefinition.createDefinitions(
+                            null,
+                            
Arrays.stream(commandLine.getOptionValues(SUBJECT_ALTERNATIVE_NAMES)).flatMap(s 
-> Arrays.stream(s.split(",")).map(String::trim)),
+                            null,
+                            null,
+                            null
+                            ));
+        } else {
+            domainAlternativeNames = Collections.EMPTY_LIST;
+        }
+
         return commandLine;
     }
 
@@ -120,8 +138,11 @@ public class TlsCertificateAuthorityClientCommandLine 
extends BaseCertificateAut
         return certificateDirectory;
     }
 
-    public String getDomainAlternativeNames() {
-        return domainAlternativeNames;
+    public List<String> getDomainAlternativeNames() {
+        if (domainAlternativeNames == null) {
+            domainAlternativeNames = Collections.EMPTY_LIST;
+        }
+        return 
domainAlternativeNames.stream().map(InstanceDefinition::getHostname).collect(Collectors.toList());
     }
 
     public TlsClientConfig createClientConfig() throws IOException {
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 14dc46f..68c8ffe 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
@@ -58,7 +58,7 @@ public class TlsCertificateSigningRequestPerformer {
     private final Supplier<HttpClientBuilder> httpClientBuilderSupplier;
     private final String caHostname;
     private final String dn;
-    private final String domainAlternativeNames;
+    private final List<String> domainAlternativeNames;
     private final String token;
     private final int port;
     private final ObjectMapper objectMapper;
@@ -75,7 +75,7 @@ public class TlsCertificateSigningRequestPerformer {
     }
 
     private TlsCertificateSigningRequestPerformer(Supplier<HttpClientBuilder> 
httpClientBuilderSupplier, String caHostname,
-                                                  String dn, String 
domainAlternativeNames, String token, int port, String signingAlgorithm) {
+                                                  String dn, List<String> 
domainAlternativeNames, String token, int port, String signingAlgorithm) {
         this.httpClientBuilderSupplier = httpClientBuilderSupplier;
         this.caHostname = caHostname;
         this.dn = CertificateUtils.reorderDn(dn);
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 7407e5d..e460ca1 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
@@ -55,8 +55,11 @@ import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 
 public class TlsToolkitStandalone {
     public static final String NIFI_KEY = "nifi-key";
@@ -193,7 +196,11 @@ public class TlsToolkitStandalone {
         if (instanceDefinitions.isEmpty() && logger.isInfoEnabled()) {
             logger.info("No " + TlsToolkitStandaloneCommandLine.HOSTNAMES_ARG 
+ " specified, not generating any host certificates or configuration.");
         }
-        for (InstanceDefinition instanceDefinition : instanceDefinitions) {
+
+        List<String> domainAlternativeNames = 
standaloneConfig.getDomainAlternativeNames();
+
+        for (Integer instanceIndex : IntStream.range(0, 
instanceDefinitions.size()).boxed().collect(Collectors.toList())) {
+            InstanceDefinition instanceDefinition = 
instanceDefinitions.get(instanceIndex);
             String hostname = instanceDefinition.getHostname();
             File hostDir;
             int hostIdentifierNumber = 
instanceDefinition.getInstanceIdentifier().getNumber();
@@ -207,6 +214,16 @@ public class TlsToolkitStandalone {
             File keystore = new File(hostDir, "keystore." + 
tlsClientConfig.getKeyStoreType().toLowerCase());
             File truststore = new File(hostDir, "truststore." + 
tlsClientConfig.getTrustStoreType().toLowerCase());
 
+            // Adjust the SANs when ranges match.
+            if (domainAlternativeNames.size() == 1) {
+                
tlsClientConfig.setDomainAlternativeNames(Collections.singletonList(domainAlternativeNames.get(0)));
+            } else if (domainAlternativeNames.size() == 
instanceDefinitions.size()) {
+                
tlsClientConfig.setDomainAlternativeNames(Collections.singletonList(domainAlternativeNames.get(instanceIndex)));
+                logger.info("Using alternate name " + 
domainAlternativeNames.get(instanceIndex) + " with hostname " + hostname + ".");
+            } else if (domainAlternativeNames.size() > 0) {
+                logger.warn("Hostname count does not match given alternate 
name count. Verify names in resulting certificate.");
+            }
+
             if (hostDir.exists()) {
                 if (!hostDir.isDirectory()) {
                     throw new IOException(hostDir + " exists but is not a 
directory.");
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 f4c4d8b..9919528 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
@@ -92,7 +92,7 @@ public class TlsToolkitStandaloneCommandLine extends 
BaseTlsToolkitCommandLine {
     private String splitKeystoreFile;
     private String dnPrefix;
     private String dnSuffix;
-    private String domainAlternativeNames;
+    private List<InstanceDefinition> domainAlternativeNames;
     private String additionalCACertificatePath;
     private String keyPassword;
     private String keyStorePassword;
@@ -129,6 +129,9 @@ public class TlsToolkitStandaloneCommandLine extends 
BaseTlsToolkitCommandLine {
             tlsToolkitStandaloneCommandLine.parse(args);
         } catch (CommandLineParseException e) {
             System.exit(e.getExitCode().ordinal());
+        } catch (IllegalArgumentException e) {
+            tlsToolkitStandaloneCommandLine.printUsage("Error parsing command 
line. (" + e.getMessage() + ")");
+            System.exit(ExitCode.ERROR_PARSING_INT_ARG.ordinal());
         }
 
         if(tlsToolkitStandaloneCommandLine.splitKeystore) {
@@ -159,7 +162,6 @@ public class TlsToolkitStandaloneCommandLine extends 
BaseTlsToolkitCommandLine {
 
         dnPrefix = commandLine.getOptionValue(NIFI_DN_PREFIX_ARG, 
TlsConfig.DEFAULT_DN_PREFIX);
         dnSuffix = commandLine.getOptionValue(NIFI_DN_SUFFIX_ARG, 
TlsConfig.DEFAULT_DN_SUFFIX);
-        domainAlternativeNames = 
commandLine.getOptionValue(SUBJECT_ALTERNATIVE_NAMES_ARG);
 
         Stream<String> globalOrderExpressions = null;
         if (commandLine.hasOption(GLOBAL_PORT_SEQUENCE_ARG)) {
@@ -177,6 +179,17 @@ public class TlsToolkitStandaloneCommandLine extends 
BaseTlsToolkitCommandLine {
             instanceDefinitions = Collections.emptyList();
         }
 
+        if (commandLine.hasOption(SUBJECT_ALTERNATIVE_NAMES_ARG)) {
+            domainAlternativeNames = Collections.unmodifiableList(
+                    
InstanceDefinition.createDefinitions(globalOrderExpressions,
+                            
Arrays.stream(commandLine.getOptionValues(SUBJECT_ALTERNATIVE_NAMES_ARG)).flatMap(s
 -> Arrays.stream(s.split(",")).map(String::trim)),
+                            parsePasswordSupplier(commandLine, 
KEY_STORE_PASSWORD_ARG, passwordUtil.passwordSupplier()),
+                            parsePasswordSupplier(commandLine, 
KEY_PASSWORD_ARG, 
commandLine.hasOption(DIFFERENT_KEY_AND_KEYSTORE_PASSWORDS_ARG) ? 
passwordUtil.passwordSupplier() : null),
+                            parsePasswordSupplier(commandLine, 
TRUST_STORE_PASSWORD_ARG, passwordUtil.passwordSupplier())));
+        } else {
+            domainAlternativeNames = Collections.emptyList();
+        }
+
         String[] clientDnValues = 
commandLine.getOptionValues(CLIENT_CERT_DN_ARG);
         if (clientDnValues != null) {
             clientDns = 
Collections.unmodifiableList(Arrays.stream(clientDnValues).collect(Collectors.toList()));
@@ -268,7 +281,7 @@ public class TlsToolkitStandaloneCommandLine extends 
BaseTlsToolkitCommandLine {
         standaloneConfig.setDays(getDays());
         standaloneConfig.setDnPrefix(dnPrefix);
         standaloneConfig.setDnSuffix(dnSuffix);
-        standaloneConfig.setDomainAlternativeNames(domainAlternativeNames);
+        
standaloneConfig.setDomainAlternativeNames(domainAlternativeNames.stream().map(InstanceDefinition::getHostname).collect(Collectors.toList()));
         
standaloneConfig.setAdditionalCACertificate(additionalCACertificatePath);
         standaloneConfig.initDefaults();
 
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 3ce3897..9ed8977 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,7 +17,6 @@
 
 package org.apache.nifi.toolkit.tls.util;
 
-import org.apache.commons.lang3.StringUtils;
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
@@ -340,7 +339,7 @@ public class TlsHelper {
         return createKeyPairGenerator(algorithm, keySize).generateKeyPair();
     }
 
-    public static JcaPKCS10CertificationRequest 
generateCertificationRequest(String requestedDn, String domainAlternativeNames,
+    public static JcaPKCS10CertificationRequest 
generateCertificationRequest(String requestedDn, List<String> 
domainAlternativeNames,
                                                                              
KeyPair keyPair, String signingAlgorithm) throws OperatorCreationException {
         JcaPKCS10CertificationRequestBuilder 
jcaPKCS10CertificationRequestBuilder = new 
JcaPKCS10CertificationRequestBuilder(new X500Name(requestedDn), 
keyPair.getPublic());
 
@@ -355,7 +354,7 @@ public class TlsHelper {
         return new 
JcaPKCS10CertificationRequest(jcaPKCS10CertificationRequestBuilder.build(jcaContentSignerBuilder.build(keyPair.getPrivate())));
     }
 
-    public static Extensions createDomainAlternativeNamesExtensions(String 
domainAlternativeNames, String requestedDn) throws IOException {
+    public static Extensions 
createDomainAlternativeNamesExtensions(List<String> domainAlternativeNames, 
String requestedDn) throws IOException {
         List<GeneralName> namesList = new ArrayList<>();
 
         try {
@@ -365,10 +364,10 @@ public class TlsHelper {
             throw new IOException("Failed to extract CN from request DN: " + 
requestedDn, e);
         }
 
-        if (StringUtils.isNotBlank(domainAlternativeNames)) {
-            for (String alternativeName : domainAlternativeNames.split(",")) {
-                namesList.add(new 
GeneralName(IPAddress.isValid(alternativeName) ? GeneralName.iPAddress : 
GeneralName.dNSName, alternativeName));
-            }
+        if (domainAlternativeNames != null) {
+            for (String alternativeName : domainAlternativeNames) {
+                 namesList.add(new 
GeneralName(IPAddress.isValid(alternativeName) ? GeneralName.iPAddress : 
GeneralName.dNSName, alternativeName));
+             }
         }
 
         GeneralNames subjectAltNames = new GeneralNames(namesList.toArray(new 
GeneralName[]{}));
diff --git 
a/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/configuration/InstanceIdentifierTest.java
 
b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/configuration/InstanceIdentifierTest.java
index b75c02f..c00e7ea 100644
--- 
a/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/configuration/InstanceIdentifierTest.java
+++ 
b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/configuration/InstanceIdentifierTest.java
@@ -44,7 +44,7 @@ public class InstanceIdentifierTest {
 
     @Test
     public void testExtractHostnamesLowGreaterThanHigh() {
-        testExtractHostnames("test[3-1]");
+        testExtractHostnames("test[3-1]", "test3", "test2", "test1");
     }
 
     @Test
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 18f2e40..496e452 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
@@ -138,7 +138,7 @@ public class TlsToolkitStandaloneCommandLineTest {
     public void testSAN() throws CommandLineParseException, IOException {
         String dnsSAN = "nifi.apache.org";
         tlsToolkitStandaloneCommandLine.parse("--subjectAlternativeNames", 
dnsSAN);
-        assertEquals(dnsSAN, 
tlsToolkitStandaloneCommandLine.createConfig().getDomainAlternativeNames());
+        assertEquals(dnsSAN, 
tlsToolkitStandaloneCommandLine.createConfig().getDomainAlternativeNames().get(0));
     }
 
     @Test
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 d273cbe..62aa838 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
@@ -26,13 +26,17 @@ import org.apache.nifi.toolkit.tls.SystemExitCapturer;
 import org.apache.nifi.toolkit.tls.commandLine.BaseTlsToolkitCommandLine;
 import org.apache.nifi.toolkit.tls.commandLine.ExitCode;
 import org.apache.nifi.toolkit.tls.configuration.TlsConfig;
+import org.apache.nifi.toolkit.tls.configuration.InstanceIdentifier;
 import org.apache.nifi.toolkit.tls.service.TlsCertificateAuthorityTest;
 import org.apache.nifi.toolkit.tls.util.TlsHelper;
 import org.apache.nifi.toolkit.tls.util.TlsHelperTest;
 import org.apache.nifi.util.NiFiProperties;
+import org.bouncycastle.asn1.x509.GeneralName;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -48,18 +52,24 @@ import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 import java.security.spec.InvalidKeySpecException;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.List;
 import java.util.Properties;
 import java.util.UUID;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 public class TlsToolkitStandaloneTest {
     public static final String NIFI_FAKE_PROPERTY = "nifi.fake.property";
     public static final String FAKE_VALUE = "fake value";
     public static final String TEST_NIFI_PROPERTIES = 
"src/test/resources/localhost/nifi.properties";
+    public static final Logger logger = 
LoggerFactory.getLogger(TlsToolkitStandaloneTest.class);
     private SystemExitCapturer systemExitCapturer;
 
     private File tempDir;
@@ -223,6 +233,213 @@ public class TlsToolkitStandaloneTest {
         runAndAssertExitCode(ExitCode.ERROR_GENERATING_CONFIG, "-o", 
tempDir.getAbsolutePath(), "-C", clientDn);
     }
 
+    @Test
+    public void testStaticHostnameNoSan() throws Exception {
+        String hostname = "static.nifi.apache.org";
+        runAndAssertExitCode(ExitCode.SUCCESS, "-o", 
tempDir.getAbsolutePath(), "-n", hostname);
+
+        X509Certificate x509Certificate = 
checkLoadCertPrivateKey(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM);
+        Certificate[] certificateChain = loadCertificateChain(hostname, 
x509Certificate);
+        X509Certificate clientCert = (X509Certificate) certificateChain[0];
+        Collection<List<?>> clientSaNames = 
clientCert.getSubjectAlternativeNames();
+
+        // Must have one san that matches
+        assertEquals(1, clientSaNames.size());
+        List<?> firstSan = clientSaNames.toArray(new List<?>[0])[0];
+        assertEquals(GeneralName.dNSName, firstSan.get(0));
+        assertEquals(hostname, firstSan.get(1));
+    }
+
+    @Test
+    public void testStaticHostnameStaticSan() throws Exception {
+        String hostname = "static.nifi.apache.org";
+        String san = "alternative.nifi.apache.org";
+
+        runAndAssertExitCode(ExitCode.SUCCESS, "-o", 
tempDir.getAbsolutePath(), "-n", hostname, "--subjectAlternativeName", san);
+
+        X509Certificate x509Certificate = 
checkLoadCertPrivateKey(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM);
+        Certificate[] certificateChain = loadCertificateChain(hostname, 
x509Certificate);
+        X509Certificate clientCert = (X509Certificate) certificateChain[0];
+        Collection<List<?>> clientSaNames = 
clientCert.getSubjectAlternativeNames();
+
+        // Must have two sans, and one san that matches
+        assertEquals(2, clientSaNames.size());
+        List<?> explicitSan = clientSaNames.toArray(new List<?>[0])[1];
+        assertEquals(GeneralName.dNSName, explicitSan.get(0));
+        assertEquals(san, explicitSan.get(1));
+    }
+
+    @Test
+    public void testDynamicHostnameStaticSan() throws Exception {
+        String nodeNames = "node[1-2].nifi.apache.org";
+        String san = "alternative.nifi.apache.org";
+
+        runAndAssertExitCode(ExitCode.SUCCESS, "-o", 
tempDir.getAbsolutePath(), "-n", nodeNames, "--subjectAlternativeName", san);
+        X509Certificate x509Certificate = 
checkLoadCertPrivateKey(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM);
+        Stream<InstanceIdentifier> hostIds = 
InstanceIdentifier.createIdentifiers(Arrays.stream(new String[]{nodeNames}));
+
+        for (InstanceIdentifier hostInstance : (Iterable<InstanceIdentifier>) 
hostIds::iterator) {
+            Certificate[] certificateChain = 
loadCertificateChain(hostInstance.getHostname(), x509Certificate);
+            X509Certificate clientCert = (X509Certificate) certificateChain[0];
+            Collection<List<?>> clientSaNames = 
clientCert.getSubjectAlternativeNames();
+
+            // Must have two sans, and one san that matches
+            assertEquals(2, clientSaNames.size());
+            List<?> explicitSan = clientSaNames.toArray(new List<?>[0])[1];
+            assertEquals(GeneralName.dNSName, explicitSan.get(0));
+            assertEquals(san, explicitSan.get(1));
+        }
+    }
+
+    @Test
+    public void testDynamicHostnameDynamicSansSameRange() throws Exception {
+        String nodeNames = "node[1-2].nifi.apache.org";
+        String saNames = "alternative[1-2].nifi.apache.org";
+
+        runAndAssertExitCode(ExitCode.SUCCESS, "-o", 
tempDir.getAbsolutePath(), "-n", nodeNames, "--subjectAlternativeName", 
saNames);
+        X509Certificate x509Certificate = 
checkLoadCertPrivateKey(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM);
+
+        Stream<InstanceIdentifier> hostIds = 
InstanceIdentifier.createIdentifiers(Arrays.stream(new String[]{nodeNames}));
+        Stream<InstanceIdentifier> sansIds = 
InstanceIdentifier.createIdentifiers(Arrays.stream(new String[]{saNames}));
+
+        String[] nodeHosts = 
hostIds.map(InstanceIdentifier::getHostname).toArray(String[]::new);
+        String[] sanHosts = 
sansIds.map(InstanceIdentifier::getHostname).toArray(String[]::new);
+        assertEquals(nodeHosts.length, sanHosts.length);
+
+        for (int i = 0; i< nodeHosts.length; i++) {
+            String host = nodeHosts[i];
+            String san = sanHosts[i];
+
+            Certificate[] certificateChain = loadCertificateChain(host, 
x509Certificate);
+            X509Certificate clientCert = (X509Certificate) certificateChain[0];
+            Collection<List<?>> clientSaNames = 
clientCert.getSubjectAlternativeNames();
+
+            // Must have two sans, and both must match
+            assertEquals(2, clientSaNames.size());
+
+            List<?> hostSan = clientSaNames.toArray(new List<?>[0])[0];
+            assertEquals(GeneralName.dNSName, hostSan.get(0));
+            assertEquals(host, hostSan.get(1));
+
+            List<?> altSan = clientSaNames.toArray(new List<?>[0])[1];
+            assertEquals(GeneralName.dNSName, altSan.get(0));
+            assertEquals(san, altSan.get(1));
+        }
+    }
+
+    @Test
+    public void testDynamicHostnameDynamicSansSameRangeDiffValues() throws 
Exception {
+        String nodeNames = "node[1-2].nifi.apache.org";
+        String saNames = "alternative[3-4].nifi.apache.org";
+
+        runAndAssertExitCode(ExitCode.SUCCESS, "-o", 
tempDir.getAbsolutePath(), "-n", nodeNames, "--subjectAlternativeName", 
saNames);
+        X509Certificate x509Certificate = 
checkLoadCertPrivateKey(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM);
+
+        Stream<InstanceIdentifier> hostIds = 
InstanceIdentifier.createIdentifiers(Arrays.stream(new String[]{nodeNames}));
+        Stream<InstanceIdentifier> sansIds = 
InstanceIdentifier.createIdentifiers(Arrays.stream(new String[]{saNames}));
+
+        String[] nodeHosts = 
hostIds.map(InstanceIdentifier::getHostname).toArray(String[]::new);
+        String[] sanHosts = 
sansIds.map(InstanceIdentifier::getHostname).toArray(String[]::new);
+        assertEquals(nodeHosts.length, sanHosts.length);
+
+        for (int i = 0; i< nodeHosts.length; i++) {
+            String host = nodeHosts[i];
+            String san = sanHosts[i];
+
+            Certificate[] certificateChain = loadCertificateChain(host, 
x509Certificate);
+            X509Certificate clientCert = (X509Certificate) certificateChain[0];
+            Collection<List<?>> clientSaNames = 
clientCert.getSubjectAlternativeNames();
+
+            // Must have two sans, and both must match
+            assertEquals(2, clientSaNames.size());
+
+            List<?> hostSan = clientSaNames.toArray(new List<?>[0])[0];
+            assertEquals(GeneralName.dNSName, hostSan.get(0));
+            assertEquals(host, hostSan.get(1));
+
+            List<?> altSan = clientSaNames.toArray(new List<?>[0])[1];
+            assertEquals(GeneralName.dNSName, altSan.get(0));
+            assertEquals(san, altSan.get(1));
+        }
+    }
+
+    @Test
+    public void testDynamicHostnameDynamicSansDiffRange() throws Exception {
+        String nodeNames = "node[1-2].nifi.apache.org";
+        String saNames = "alternative[5-7].nifi.apache.org";
+
+        runAndAssertExitCode(ExitCode.SUCCESS, "-o", 
tempDir.getAbsolutePath(), "-n", nodeNames, "--subjectAlternativeName", 
saNames);
+        X509Certificate x509Certificate = 
checkLoadCertPrivateKey(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM);
+
+        Stream<InstanceIdentifier> hostIds = 
InstanceIdentifier.createIdentifiers(Arrays.stream(new String[]{nodeNames}));
+        Stream<InstanceIdentifier> sansIds = 
InstanceIdentifier.createIdentifiers(Arrays.stream(new String[]{saNames}));
+
+        String[] nodeHosts = 
hostIds.map(InstanceIdentifier::getHostname).toArray(String[]::new);
+        String[] sanHosts = 
sansIds.map(InstanceIdentifier::getHostname).toArray(String[]::new);
+        assertEquals(2, nodeHosts.length);
+        assertEquals(3, sanHosts.length);
+
+        for (int i = 0; i< nodeHosts.length; i++) {
+            String host = nodeHosts[i];
+
+            Certificate[] certificateChain = loadCertificateChain(host, 
x509Certificate);
+            X509Certificate clientCert = (X509Certificate) certificateChain[0];
+            Collection<List<?>> clientSaNames = 
clientCert.getSubjectAlternativeNames();
+
+            // Must have sans + cn
+            assertEquals(1 + sanHosts.length, clientSaNames.size());
+
+            for (int j = 0; j < sanHosts.length; j++) {
+                String sanHost = 
clientSaNames.stream().collect(Collectors.toList()).get(j+1).get(1).toString();
+                assertEquals(sanHosts[j], sanHost);
+            }
+        }
+    }
+
+    @Test
+    public void testDynamicHostnameDynamicSansSameRangeReverseOrder() throws 
Exception {
+        String nodeNames = "node[1-2].nifi.apache.org";
+        String saNames = "alternative[2-1].nifi.apache.org";
+
+        runAndAssertExitCode(ExitCode.SUCCESS, "-o", 
tempDir.getAbsolutePath(), "-n", nodeNames, "--subjectAlternativeName", 
saNames);
+        X509Certificate x509Certificate = 
checkLoadCertPrivateKey(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM);
+
+        Stream<InstanceIdentifier> hostIds = 
InstanceIdentifier.createIdentifiers(Arrays.stream(new String[]{nodeNames}));
+        Stream<InstanceIdentifier> sansIds = 
InstanceIdentifier.createIdentifiers(Arrays.stream(new String[]{saNames}));
+
+        String[] nodeHosts = 
hostIds.map(InstanceIdentifier::getHostname).toArray(String[]::new);
+        String[] sanHosts = 
sansIds.map(InstanceIdentifier::getHostname).toArray(String[]::new);
+        assertTrue(nodeHosts.length > 0);
+        assertEquals(nodeHosts.length, sanHosts.length);
+
+        for (int i = 0; i< nodeHosts.length; i++) {
+            String host = nodeHosts[i];
+            String san = sanHosts[i];
+
+            Certificate[] certificateChain = loadCertificateChain(host, 
x509Certificate);
+            X509Certificate clientCert = (X509Certificate) certificateChain[0];
+            Collection<List<?>> clientSaNames = 
clientCert.getSubjectAlternativeNames();
+
+            // Must have sans + cn
+            assertEquals(2, clientSaNames.size());
+
+            List<?> hostSan = clientSaNames.toArray(new List<?>[0])[0];
+            assertEquals(GeneralName.dNSName, hostSan.get(0));
+            assertEquals(host, hostSan.get(1));
+
+            List<?> altSan = clientSaNames.toArray(new List<?>[0])[1];
+            assertEquals(GeneralName.dNSName, altSan.get(0));
+            assertEquals(san, altSan.get(1));
+        }
+    }
+
+    @Test
+    public void testDynamicHostnameDynamicSansNonNumeric() throws Exception {
+        String nodeNames = "node[1-2].nifi.apache.org";
+        String saNames = "alternative[A-B].nifi.apache.org";
+        runAndAssertExitCode(ExitCode.ERROR_PARSING_INT_ARG, "-o", 
tempDir.getAbsolutePath(), "-n", nodeNames, "--subjectAlternativeName", 
saNames);
+    }
+
     private X509Certificate checkLoadCertPrivateKey(String algorithm) throws 
IOException, NoSuchAlgorithmException, InvalidKeySpecException, 
CertificateException {
         KeyPair keyPair = TlsHelperTest.loadKeyPair(new File(tempDir, 
TlsToolkitStandalone.NIFI_KEY + ".key"));
 
@@ -317,6 +534,21 @@ public class TlsToolkitStandaloneTest {
 
     }
 
+    private Certificate[] loadCertificateChain(String hostname, 
X509Certificate rootCert) throws Exception {
+        File hostDir = new File(tempDir, hostname);
+        Properties nifiProperties = 
checkHostDirAndReturnNifiProperties(hostname, rootCert);
+        String keyStoreType = 
nifiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE_TYPE);
+        String keyStoreFilename = BaseTlsToolkitCommandLine.KEYSTORE + 
keyStoreType;
+        File keyStoreFile = new File(hostDir, keyStoreFilename);
+        KeyStore keyStore = KeyStoreUtils.getKeyStore(keyStoreType);
+
+        try (FileInputStream fileInputStream = new 
FileInputStream(keyStoreFile)) {
+            keyStore.load(fileInputStream, 
nifiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD).toCharArray());
+        }
+
+        return keyStore.getCertificateChain(TlsToolkitStandalone.NIFI_KEY);
+    }
+
     private void runAndAssertExitCode(ExitCode exitCode, String... args) {
         systemExitCapturer.runAndAssertExitCode(() -> 
TlsToolkitStandaloneCommandLine.main(args), exitCode);
     }
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 490a3b7..904086e 100644
--- 
a/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/util/TlsHelperTest.java
+++ 
b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/util/TlsHelperTest.java
@@ -17,7 +17,6 @@
 
 package org.apache.nifi.toolkit.tls.util;
 
-import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.security.util.CertificateUtils;
 import org.apache.nifi.toolkit.tls.configuration.TlsConfig;
 import org.bouncycastle.asn1.pkcs.Attribute;
@@ -355,15 +354,14 @@ public class TlsHelperTest {
     public void testShouldIncludeSANFromCSR() throws Exception {
         // Arrange
         final List<String> SAN_ENTRIES = Arrays.asList("127.0.0.1", 
"nifi.nifi.apache.org");
-        final String SAN = StringUtils.join(SAN_ENTRIES, ",");
         final int SAN_COUNT = SAN_ENTRIES.size();
         final String DN = "CN=localhost";
         KeyPair keyPair = keyPairGenerator.generateKeyPair();
         logger.info("Generating CSR with DN: " + DN);
 
         // Act
-        JcaPKCS10CertificationRequest csrWithSan = 
TlsHelper.generateCertificationRequest(DN, SAN, keyPair, 
TlsConfig.DEFAULT_SIGNING_ALGORITHM);
-        logger.info("Created CSR with SAN: " + SAN);
+        JcaPKCS10CertificationRequest csrWithSan = 
TlsHelper.generateCertificationRequest(DN, SAN_ENTRIES, keyPair, 
TlsConfig.DEFAULT_SIGNING_ALGORITHM);
+        logger.info("Created CSR with SAN: " + SAN_ENTRIES);
         String testCsrPem = TlsHelper.pemEncodeJcaObject(csrWithSan);
         logger.info("Encoded CSR as PEM: " + testCsrPem);
 

Reply via email to