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:
mtien 2019-11-04 16:08:07 -08:00 committed by Andy LoPresto
parent 49b7a7cd6b
commit 36bdb474c3
No known key found for this signature in database
GPG Key ID: 6EC293152D90B61D
4 changed files with 162 additions and 6 deletions

View File

@ -26,6 +26,7 @@ import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Arrays;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
@ -42,6 +43,7 @@ public class SSLContextFactory {
private final String keystore;
private final char[] keystorePass;
private final String keystoreType;
private final char[] keyPassword;
private final String truststore;
private final char[] truststorePass;
private final String truststoreType;
@ -53,6 +55,7 @@ public class SSLContextFactory {
keystore = properties.getProperty(NiFiProperties.SECURITY_KEYSTORE);
keystorePass = getPass(properties.getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD));
keystoreType = properties.getProperty(NiFiProperties.SECURITY_KEYSTORE_TYPE);
keyPassword = getPass(properties.getProperty(NiFiProperties.SECURITY_KEY_PASSWD));
truststore = properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE);
truststorePass = getPass(properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD));
@ -67,7 +70,11 @@ public class SSLContextFactory {
FileUtils.closeQuietly(keyStoreStream);
}
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
final KeyStore trustStore = KeyStoreUtils.getTrustStore(truststoreType);
@ -91,14 +98,13 @@ public class SSLContextFactory {
/**
* Creates a SSLContext instance using the given information.
*
*
* @return a SSLContext instance
* @throws java.security.KeyStoreException if problem with keystore
* @throws java.io.IOException if unable to create context
* @throws java.security.NoSuchAlgorithmException if algorithm isn't known
* @throws java.security.KeyStoreException if problem with keystore
* @throws java.io.IOException if unable to create context
* @throws java.security.NoSuchAlgorithmException if algorithm isn't known
* @throws java.security.cert.CertificateException if certificate is invalid
* @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,
UnrecoverableKeyException, KeyManagementException {

View File

@ -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
}
}