mirror of https://github.com/apache/nifi.git
NIFI-6830 Added regression test. Added failing unit test. Added Test Resources.
Implemented KeyPassword detection in the SSLContextFactory. Resolved Java 8/11 unit test issue. Fixed unit test wildcard imports and added Javadoc to test helper methods. This closes #3873. Signed-off-by: Andy LoPresto <alopresto@apache.org>
This commit is contained in:
parent
49b7a7cd6b
commit
36bdb474c3
|
@ -26,6 +26,7 @@ import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.security.UnrecoverableKeyException;
|
import java.security.UnrecoverableKeyException;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import javax.net.ssl.KeyManager;
|
import javax.net.ssl.KeyManager;
|
||||||
import javax.net.ssl.KeyManagerFactory;
|
import javax.net.ssl.KeyManagerFactory;
|
||||||
|
@ -42,6 +43,7 @@ public class SSLContextFactory {
|
||||||
private final String keystore;
|
private final String keystore;
|
||||||
private final char[] keystorePass;
|
private final char[] keystorePass;
|
||||||
private final String keystoreType;
|
private final String keystoreType;
|
||||||
|
private final char[] keyPassword;
|
||||||
private final String truststore;
|
private final String truststore;
|
||||||
private final char[] truststorePass;
|
private final char[] truststorePass;
|
||||||
private final String truststoreType;
|
private final String truststoreType;
|
||||||
|
@ -53,6 +55,7 @@ public class SSLContextFactory {
|
||||||
keystore = properties.getProperty(NiFiProperties.SECURITY_KEYSTORE);
|
keystore = properties.getProperty(NiFiProperties.SECURITY_KEYSTORE);
|
||||||
keystorePass = getPass(properties.getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD));
|
keystorePass = getPass(properties.getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD));
|
||||||
keystoreType = properties.getProperty(NiFiProperties.SECURITY_KEYSTORE_TYPE);
|
keystoreType = properties.getProperty(NiFiProperties.SECURITY_KEYSTORE_TYPE);
|
||||||
|
keyPassword = getPass(properties.getProperty(NiFiProperties.SECURITY_KEY_PASSWD));
|
||||||
|
|
||||||
truststore = properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE);
|
truststore = properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE);
|
||||||
truststorePass = getPass(properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD));
|
truststorePass = getPass(properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD));
|
||||||
|
@ -67,7 +70,11 @@ public class SSLContextFactory {
|
||||||
FileUtils.closeQuietly(keyStoreStream);
|
FileUtils.closeQuietly(keyStoreStream);
|
||||||
}
|
}
|
||||||
final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||||
keyManagerFactory.init(keyStore, keystorePass);
|
if (keyPassword != null && !Arrays.equals(keyPassword, keystorePass)) {
|
||||||
|
keyManagerFactory.init(keyStore, keyPassword);
|
||||||
|
} else {
|
||||||
|
keyManagerFactory.init(keyStore, keystorePass);
|
||||||
|
}
|
||||||
|
|
||||||
// prepare the truststore
|
// prepare the truststore
|
||||||
final KeyStore trustStore = KeyStoreUtils.getTrustStore(truststoreType);
|
final KeyStore trustStore = KeyStoreUtils.getTrustStore(truststoreType);
|
||||||
|
@ -91,14 +98,13 @@ public class SSLContextFactory {
|
||||||
/**
|
/**
|
||||||
* Creates a SSLContext instance using the given information.
|
* Creates a SSLContext instance using the given information.
|
||||||
*
|
*
|
||||||
*
|
|
||||||
* @return a SSLContext instance
|
* @return a SSLContext instance
|
||||||
* @throws java.security.KeyStoreException if problem with keystore
|
* @throws java.security.KeyStoreException if problem with keystore
|
||||||
* @throws java.io.IOException if unable to create context
|
* @throws java.io.IOException if unable to create context
|
||||||
* @throws java.security.NoSuchAlgorithmException if algorithm isn't known
|
* @throws java.security.NoSuchAlgorithmException if algorithm isn't known
|
||||||
* @throws java.security.cert.CertificateException if certificate is invalid
|
* @throws java.security.cert.CertificateException if certificate is invalid
|
||||||
* @throws java.security.UnrecoverableKeyException if the key cannot be recovered
|
* @throws java.security.UnrecoverableKeyException if the key cannot be recovered
|
||||||
* @throws java.security.KeyManagementException if the key is improper
|
* @throws java.security.KeyManagementException if the key is improper
|
||||||
*/
|
*/
|
||||||
public SSLContext createSslContext() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException,
|
public SSLContext createSslContext() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException,
|
||||||
UnrecoverableKeyException, KeyManagementException {
|
UnrecoverableKeyException, KeyManagementException {
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
/*
|
||||||
|
* 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.io.socket
|
||||||
|
|
||||||
|
import org.apache.nifi.util.NiFiProperties
|
||||||
|
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.AfterClass
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.BeforeClass
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.junit.runners.JUnit4
|
||||||
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLContext
|
||||||
|
import java.security.Security
|
||||||
|
|
||||||
|
@RunWith(JUnit4.class)
|
||||||
|
class SSLContextFactoryTest extends GroovyTestCase {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(SSLContextFactoryTest.class)
|
||||||
|
|
||||||
|
private static String NF_PROPS_FILE = null
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
static void setUpOnce() throws Exception {
|
||||||
|
Security.addProvider(new BouncyCastleProvider())
|
||||||
|
|
||||||
|
if (System.getProperty(NiFiProperties.PROPERTIES_FILE_PATH)) {
|
||||||
|
NF_PROPS_FILE = System.getProperty(NiFiProperties.PROPERTIES_FILE_PATH)
|
||||||
|
System.setProperty(NiFiProperties.PROPERTIES_FILE_PATH, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.metaClass.methodMissing = { String name, args ->
|
||||||
|
logger.info("[${name?.toUpperCase()}] ${(args as List).join(" ")}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
void setUp() throws Exception {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
void tearDown() throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
static void tearDownOnce() throws Exception {
|
||||||
|
if (NF_PROPS_FILE) {
|
||||||
|
System.setProperty(NiFiProperties.PROPERTIES_FILE_PATH, NF_PROPS_FILE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link NiFiProperties} object configured with default values for accessing
|
||||||
|
* keystores and truststores. The values can be overridden by providing a map parameter.
|
||||||
|
*
|
||||||
|
* @param overrides an optional Map of overriding configuration values
|
||||||
|
* @return the configured NiFiProperties object
|
||||||
|
*/
|
||||||
|
private static NiFiProperties buildNiFiProperties(Map<String, String> overrides = [:]) {
|
||||||
|
final Map DEFAULTS = [
|
||||||
|
(NiFiProperties.SECURITY_KEYSTORE_PASSWD) : "keystorepassword",
|
||||||
|
(NiFiProperties.SECURITY_KEYSTORE) : "src/test/resources/samepassword.jks",
|
||||||
|
(NiFiProperties.SECURITY_KEYSTORE_TYPE) : "JKS",
|
||||||
|
(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD): "changeit",
|
||||||
|
(NiFiProperties.SECURITY_TRUSTSTORE) : buildCacertsPath(),
|
||||||
|
(NiFiProperties.SECURITY_TRUSTSTORE_TYPE) : "JKS",
|
||||||
|
]
|
||||||
|
DEFAULTS.putAll(overrides)
|
||||||
|
NiFiProperties.createBasicNiFiProperties(null, DEFAULTS)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the file path to the {@code cacerts} default JRE truststore. Handles Java 8
|
||||||
|
* and earlier as well as Java 9 and later directory structures.
|
||||||
|
*
|
||||||
|
* @return the path to cacerts
|
||||||
|
*/
|
||||||
|
private static String buildCacertsPath() {
|
||||||
|
String javaHome = System.getenv("JAVA_HOME")
|
||||||
|
if (System.getProperty("java.version").startsWith("1.")) {
|
||||||
|
javaHome + "/jre/lib/security/cacerts"
|
||||||
|
} else {
|
||||||
|
javaHome + "/lib/security/cacerts"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testShouldVerifyKeystoreWithSameKeyPassword() throws Exception {
|
||||||
|
// Arrange
|
||||||
|
|
||||||
|
// Set up the keystore configuration as NiFiProperties object
|
||||||
|
NiFiProperties np = buildNiFiProperties()
|
||||||
|
|
||||||
|
// Create the SSLContextFactory with the config
|
||||||
|
SSLContextFactory sslcf = new SSLContextFactory(np)
|
||||||
|
|
||||||
|
// Act
|
||||||
|
|
||||||
|
// Access the SSLContextFactory to create an SSLContext
|
||||||
|
SSLContext sslContext = sslcf.createSslContext()
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
|
||||||
|
// The SSLContext was accessible and correct
|
||||||
|
assert sslContext
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testShouldVerifyKeystoreWithDifferentKeyPassword() throws Exception {
|
||||||
|
// Arrange
|
||||||
|
|
||||||
|
// Set up the keystore configuration as NiFiProperties object
|
||||||
|
// (prior to NIFI-6830, an UnrecoverableKeyException was thrown due to the wrong password being provided)
|
||||||
|
NiFiProperties np = buildNiFiProperties([
|
||||||
|
(NiFiProperties.SECURITY_KEYSTORE) : "src/test/resources/differentpassword.jks",
|
||||||
|
(NiFiProperties.SECURITY_KEY_PASSWD): "keypassword",
|
||||||
|
])
|
||||||
|
|
||||||
|
// Create the SSLContextFactory with the config
|
||||||
|
SSLContextFactory sslcf = new SSLContextFactory(np)
|
||||||
|
|
||||||
|
// Act
|
||||||
|
|
||||||
|
// Access the SSLContextFactory to create an SSLContext
|
||||||
|
SSLContext sslContext = sslcf.createSslContext()
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
|
||||||
|
// The SSLContext was accessible and correct
|
||||||
|
assert sslContext
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue