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);