mirror of
https://github.com/apache/nifi.git
synced 2025-03-03 07:59:15 +00:00
NIFI-1355 Implemented new methods in KeyStoreUtils to programmatically-generate certificates, Keystores, and Truststores and return it wrapped in a TLS configuration.
Updated TestInvokeHTTP, TestInvokeHttpSSL, TestInvokeHttpTwoWaySSL, and TestListenHTTP to use new Keystore functionality. NIFI-1355 Refactored and removed unnecessary unit tests in KeyStoreUtilsGroovyTest. NIFI-1355 Added a password requirement when creating a new truststore. Handled exception when loading a passwordless truststore type of Bouncy Castle PKCS12. This closes #4801 Signed-off-by: David Handermann <exceptionfactory@apache.org>
This commit is contained in:
parent
5a8b18b12d
commit
6e1f737c53
@ -19,23 +19,35 @@ package org.apache.nifi.security.util;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Security;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLParameters;
|
||||
import javax.net.ssl.SSLServerSocket;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
@ -46,8 +58,24 @@ public class KeyStoreUtils {
|
||||
private static final Logger logger = LoggerFactory.getLogger(KeyStoreUtils.class);
|
||||
|
||||
public static final String SUN_PROVIDER_NAME = "SUN";
|
||||
private static final String JKS_EXT = ".jks";
|
||||
private static final String PKCS12_EXT = ".p12";
|
||||
private static final String BCFKS_EXT = ".bcfks";
|
||||
private static final String KEY_ALIAS = "nifi-key";
|
||||
private static final String CERT_ALIAS = "nifi-cert";
|
||||
private static final String CERT_DN = "CN=localhost";
|
||||
private static final String KEY_ALGORITHM = "RSA";
|
||||
private static final String SIGNING_ALGORITHM = "SHA256withRSA";
|
||||
private static final int CERT_DURATION_DAYS = 365;
|
||||
private static final int PASSWORD_LENGTH = 16;
|
||||
private static final String TEST_KEYSTORE_PREFIX = "test-keystore-";
|
||||
private static final String TEST_TRUSTSTORE_PREFIX = "test-truststore-";
|
||||
|
||||
private static final String KEYSTORE_ERROR_MSG = "There was an error creating a Keystore.";
|
||||
private static final String TRUSTSTORE_ERROR_MSG = "There was an error creating a Truststore.";
|
||||
|
||||
private static final Map<String, String> KEY_STORE_TYPE_PROVIDERS = new HashMap<>();
|
||||
private static final Map<KeystoreType, String> KEY_STORE_EXTENSIONS = new HashMap<>();
|
||||
|
||||
static {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
@ -57,6 +85,12 @@ public class KeyStoreUtils {
|
||||
KEY_STORE_TYPE_PROVIDERS.put(KeystoreType.JKS.getType(), SUN_PROVIDER_NAME);
|
||||
}
|
||||
|
||||
static {
|
||||
KEY_STORE_EXTENSIONS.put(KeystoreType.JKS, JKS_EXT);
|
||||
KEY_STORE_EXTENSIONS.put(KeystoreType.PKCS12, PKCS12_EXT);
|
||||
KEY_STORE_EXTENSIONS.put(KeystoreType.BCFKS, BCFKS_EXT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the provider that will be used for the given keyStoreType
|
||||
*
|
||||
@ -111,6 +145,63 @@ public class KeyStoreUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a temporary default Keystore and Truststore and returns it wrapped in a TLS configuration.
|
||||
*
|
||||
* @return a {@link org.apache.nifi.security.util.TlsConfiguration}
|
||||
*/
|
||||
public static TlsConfiguration createTlsConfigAndNewKeystoreTruststore() throws IOException, GeneralSecurityException {
|
||||
return createTlsConfigAndNewKeystoreTruststore(new StandardTlsConfiguration());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a temporary Keystore and Truststore and returns it wrapped in a new TLS configuration with the given values.
|
||||
*
|
||||
* @param tlsConfiguration a {@link org.apache.nifi.security.util.TlsConfiguration}
|
||||
* @return a {@link org.apache.nifi.security.util.TlsConfiguration}
|
||||
*/
|
||||
public static TlsConfiguration createTlsConfigAndNewKeystoreTruststore(final TlsConfiguration tlsConfiguration) throws IOException, GeneralSecurityException {
|
||||
final Path keyStorePath;
|
||||
final String keystorePassword = StringUtils.isNotBlank(tlsConfiguration.getKeystorePassword()) ? tlsConfiguration.getKeystorePassword() : generatePassword();
|
||||
final KeystoreType keystoreType = tlsConfiguration.getKeystoreType() != null ? tlsConfiguration.getKeystoreType() : KeystoreType.PKCS12;
|
||||
final String keyPassword = StringUtils.isNotBlank(tlsConfiguration.getKeyPassword()) ? tlsConfiguration.getKeyPassword() : keystorePassword;
|
||||
final Path trustStorePath;
|
||||
final String truststorePassword = StringUtils.isNotBlank(tlsConfiguration.getTruststorePassword()) ? tlsConfiguration.getTruststorePassword() : generatePassword();
|
||||
final KeystoreType truststoreType = tlsConfiguration.getTruststoreType() != null ? tlsConfiguration.getTruststoreType() : KeystoreType.PKCS12;
|
||||
|
||||
// Create temporary Keystore file
|
||||
try {
|
||||
keyStorePath = generateTempKeystorePath(keystoreType);
|
||||
} catch (IOException e) {
|
||||
logger.error(KEYSTORE_ERROR_MSG, e);
|
||||
throw new UncheckedIOException(KEYSTORE_ERROR_MSG, e);
|
||||
}
|
||||
|
||||
// Create temporary Truststore file
|
||||
try {
|
||||
trustStorePath = generateTempTruststorePath(truststoreType);
|
||||
} catch (IOException e) {
|
||||
logger.error(TRUSTSTORE_ERROR_MSG, e);
|
||||
throw new UncheckedIOException(TRUSTSTORE_ERROR_MSG, e);
|
||||
}
|
||||
|
||||
// Create X509 Certificate
|
||||
final X509Certificate clientCert = createKeyStoreAndGetX509Certificate(KEY_ALIAS, keystorePassword, keyPassword, keyStorePath.toString(), keystoreType);
|
||||
|
||||
// Create Truststore
|
||||
createTrustStore(clientCert, CERT_ALIAS, truststorePassword, trustStorePath.toString(), truststoreType);
|
||||
|
||||
return new StandardTlsConfiguration(
|
||||
keyStorePath.toString(),
|
||||
keystorePassword,
|
||||
keyPassword,
|
||||
keystoreType,
|
||||
trustStorePath.toString(),
|
||||
truststorePassword,
|
||||
truststoreType,
|
||||
TlsPlatform.getLatestProtocol());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link KeyManagerFactory} from the provided {@link KeyStore} object, initialized with the key or keystore password.
|
||||
*
|
||||
@ -136,7 +227,7 @@ public class KeyStoreUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the intialized {@link KeyManagerFactory}.
|
||||
* Returns the initialized {@link KeyManagerFactory}.
|
||||
*
|
||||
* @param tlsConfiguration the TLS configuration
|
||||
* @return the initialized key manager factory
|
||||
@ -167,7 +258,6 @@ public class KeyStoreUtils {
|
||||
return getKeyManagerFactoryFromKeyStore(keyStore, keystorePasswordChars, keyPasswordChars);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a loaded {@link KeyStore} (acting as a truststore) given the provided configuration values.
|
||||
*
|
||||
@ -210,7 +300,7 @@ public class KeyStoreUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the intialized {@link TrustManagerFactory}.
|
||||
* Returns the initialized {@link TrustManagerFactory}.
|
||||
*
|
||||
* @param tlsConfiguration the TLS configuration
|
||||
* @return the initialized trust manager factory
|
||||
@ -230,6 +320,11 @@ public class KeyStoreUtils {
|
||||
* @throws TlsException if there is a problem initializing or reading from the truststore
|
||||
*/
|
||||
public static TrustManagerFactory loadTrustManagerFactory(String truststorePath, String truststorePassword, String truststoreType) throws TlsException {
|
||||
// Bouncy Castle PKCS12 type requires a password
|
||||
if (truststoreType.equalsIgnoreCase(KeystoreType.PKCS12.getType()) && StringUtils.isBlank(truststorePassword)) {
|
||||
throw new IllegalArgumentException("A PKCS12 Truststore Type requires a password");
|
||||
}
|
||||
|
||||
// Legacy truststore passwords can be empty
|
||||
final char[] truststorePasswordChars = StringUtils.isNotBlank(truststorePassword) ? truststorePassword.toCharArray() : null;
|
||||
KeyStore trustStore = loadTrustStore(truststorePath, truststorePasswordChars, truststoreType);
|
||||
@ -352,4 +447,116 @@ public class KeyStoreUtils {
|
||||
.append("useClientMode", sslServerSocket.getUseClientMode())
|
||||
.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the Keystore and returns a X509 Certificate with the given values.
|
||||
*
|
||||
* @param alias the certificate alias
|
||||
* @param keyStorePassword the keystore password
|
||||
* @param keyPassword the key password
|
||||
* @param keyStorePath the keystore path
|
||||
* @param keyStoreType the keystore type
|
||||
* @return a {@link X509Certificate}
|
||||
*/
|
||||
private static X509Certificate createKeyStoreAndGetX509Certificate(
|
||||
final String alias, final String keyStorePassword, final String keyPassword, final String keyStorePath,
|
||||
final KeystoreType keyStoreType) throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException {
|
||||
|
||||
try (final FileOutputStream outputStream = new FileOutputStream(keyStorePath)) {
|
||||
final KeyPair keyPair = KeyPairGenerator.getInstance(KEY_ALGORITHM).generateKeyPair();
|
||||
|
||||
final X509Certificate selfSignedCert = CertificateUtils.generateSelfSignedX509Certificate(
|
||||
keyPair, CERT_DN, SIGNING_ALGORITHM, CERT_DURATION_DAYS
|
||||
);
|
||||
|
||||
final KeyStore keyStore = loadEmptyKeyStore(keyStoreType);
|
||||
keyStore.setKeyEntry(alias, keyPair.getPrivate(), keyPassword.toCharArray(), new Certificate[]{selfSignedCert});
|
||||
keyStore.store(outputStream, keyStorePassword.toCharArray());
|
||||
|
||||
return selfSignedCert;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the Truststore with the given values.
|
||||
*
|
||||
* @param cert the certificate
|
||||
* @param alias the certificate alias
|
||||
* @param password the truststore password
|
||||
* @param path the truststore path
|
||||
* @param truststoreType the truststore type
|
||||
*/
|
||||
private static void createTrustStore(final X509Certificate cert,
|
||||
final String alias, final String password, final String path, final KeystoreType truststoreType)
|
||||
throws KeyStoreException, NoSuchAlgorithmException, CertificateException {
|
||||
|
||||
try (final FileOutputStream outputStream = new FileOutputStream(path)) {
|
||||
final KeyStore trustStore = loadEmptyKeyStore(truststoreType);
|
||||
trustStore.setCertificateEntry(alias, cert);
|
||||
trustStore.store(outputStream, password.toCharArray());
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(TRUSTSTORE_ERROR_MSG, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a temporary keystore file and returns the path.
|
||||
*
|
||||
* @param keystoreType the Keystore type
|
||||
* @return a Path
|
||||
*/
|
||||
private static Path generateTempKeystorePath(KeystoreType keystoreType) throws IOException {
|
||||
return Files.createTempFile(TEST_KEYSTORE_PREFIX, getKeystoreExtension(keystoreType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a temporary truststore file and returns the path.
|
||||
*
|
||||
* @param truststoreType the Truststore type
|
||||
* @return a Path
|
||||
*/
|
||||
private static Path generateTempTruststorePath(KeystoreType truststoreType) throws IOException {
|
||||
return Files.createTempFile(TEST_TRUSTSTORE_PREFIX, getKeystoreExtension(truststoreType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads and returns an empty Keystore backed by the appropriate provider.
|
||||
*
|
||||
* @param keyStoreType the keystore type
|
||||
* @return an empty keystore
|
||||
* @throws KeyStoreException if a keystore of the given type cannot be instantiated
|
||||
*/
|
||||
private static KeyStore loadEmptyKeyStore(KeystoreType keyStoreType) throws KeyStoreException, CertificateException, NoSuchAlgorithmException {
|
||||
final KeyStore keyStore;
|
||||
try {
|
||||
keyStore = KeyStore.getInstance(
|
||||
Objects.requireNonNull(keyStoreType).getType());
|
||||
keyStore.load(null, null);
|
||||
return keyStore;
|
||||
} catch (IOException e) {
|
||||
logger.error("Encountered an error loading keystore: {}", e.getLocalizedMessage());
|
||||
throw new UncheckedIOException("Error loading keystore", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Keystore extension given the Keystore type.
|
||||
*
|
||||
* @param keystoreType the keystore type
|
||||
* @return the keystore extension
|
||||
*/
|
||||
private static String getKeystoreExtension(KeystoreType keystoreType) {
|
||||
return KEY_STORE_EXTENSIONS.get(keystoreType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random Hex-encoded password.
|
||||
*
|
||||
* @return a password as a Hex-encoded String
|
||||
*/
|
||||
private static String generatePassword() {
|
||||
final byte[] password = new byte[PASSWORD_LENGTH];
|
||||
new SecureRandom().nextBytes(password);
|
||||
return Hex.encodeHexString(password);
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,9 @@
|
||||
*/
|
||||
package org.apache.nifi.security.util
|
||||
|
||||
import org.apache.nifi.util.StringUtils
|
||||
import org.junit.After
|
||||
import org.junit.AfterClass
|
||||
import org.junit.Before
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.Ignore
|
||||
@ -29,6 +31,8 @@ import org.slf4j.LoggerFactory
|
||||
import javax.net.ssl.HttpsURLConnection
|
||||
import javax.net.ssl.SSLSocket
|
||||
import javax.net.ssl.SSLSocketFactory
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import java.security.KeyStore
|
||||
import java.security.cert.Certificate
|
||||
|
||||
@ -36,16 +40,29 @@ import java.security.cert.Certificate
|
||||
class KeyStoreUtilsGroovyTest extends GroovyTestCase {
|
||||
private static final Logger logger = LoggerFactory.getLogger(KeyStoreUtilsGroovyTest.class)
|
||||
|
||||
private static final File KEYSTORE_FILE = new File("src/test/resources/keystore.jks")
|
||||
private static final String KEYSTORE_PASSWORD = "passwordpassword"
|
||||
private static final String KEY_PASSWORD = "keypassword"
|
||||
private static final KeystoreType KEYSTORE_TYPE = KeystoreType.JKS
|
||||
private static final String TEST_KEYSTORE_PASSWORD = "keystorepassword"
|
||||
private static final String TEST_KEY_PASSWORD = "keypassword"
|
||||
private static final String TEST_TRUSTSTORE_PASSWORD = "truststorepassword"
|
||||
private static final KeystoreType DEFAULT_STORE_TYPE = KeystoreType.JKS
|
||||
private static final KeystoreType PKCS12_STORE_TYPE = KeystoreType.PKCS12
|
||||
|
||||
private static TlsConfiguration tlsConfigParam
|
||||
private static TlsConfiguration tlsConfiguration
|
||||
|
||||
@BeforeClass
|
||||
static void setUpOnce() {
|
||||
logger.metaClass.methodMissing = { String name, args ->
|
||||
logger.info("[${name?.toUpperCase()}] ${(args as List).join(" ")}")
|
||||
}
|
||||
|
||||
tlsConfigParam = new StandardTlsConfiguration(null, TEST_KEYSTORE_PASSWORD, TEST_KEY_PASSWORD, DEFAULT_STORE_TYPE, null, TEST_TRUSTSTORE_PASSWORD, null)
|
||||
|
||||
tlsConfiguration = KeyStoreUtils.createTlsConfigAndNewKeystoreTruststore(tlsConfigParam)
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
static void afterClass() throws Exception {
|
||||
deleteKeystoreTruststore(tlsConfiguration);
|
||||
}
|
||||
|
||||
@Before
|
||||
@ -61,9 +78,10 @@ class KeyStoreUtilsGroovyTest extends GroovyTestCase {
|
||||
@Test
|
||||
void testShouldVerifyKeystoreIsValid() {
|
||||
// Arrange
|
||||
final URL ksUrl = getKeystorePathAsUrl(tlsConfiguration.getKeystorePath())
|
||||
|
||||
// Act
|
||||
boolean keystoreIsValid = KeyStoreUtils.isStoreValid(KEYSTORE_FILE.toURI().toURL(), KEYSTORE_TYPE, KEYSTORE_PASSWORD.toCharArray())
|
||||
boolean keystoreIsValid = KeyStoreUtils.isStoreValid(ksUrl, DEFAULT_STORE_TYPE, TEST_KEYSTORE_PASSWORD.toCharArray())
|
||||
|
||||
// Assert
|
||||
assert keystoreIsValid
|
||||
@ -72,9 +90,10 @@ class KeyStoreUtilsGroovyTest extends GroovyTestCase {
|
||||
@Test
|
||||
void testShouldVerifyKeystoreIsNotValid() {
|
||||
// Arrange
|
||||
final URL ksUrl = getKeystorePathAsUrl(tlsConfiguration.getKeystorePath())
|
||||
|
||||
// Act
|
||||
boolean keystoreIsValid = KeyStoreUtils.isStoreValid(KEYSTORE_FILE.toURI().toURL(), KEYSTORE_TYPE, KEYSTORE_PASSWORD.reverse().toCharArray())
|
||||
boolean keystoreIsValid = KeyStoreUtils.isStoreValid(ksUrl, DEFAULT_STORE_TYPE, TEST_KEYSTORE_PASSWORD.reverse().toCharArray())
|
||||
|
||||
// Assert
|
||||
assert !keystoreIsValid
|
||||
@ -83,9 +102,10 @@ class KeyStoreUtilsGroovyTest extends GroovyTestCase {
|
||||
@Test
|
||||
void testShouldVerifyKeyPasswordIsValid() {
|
||||
// Arrange
|
||||
final URL ksUrl = getKeystorePathAsUrl(tlsConfiguration.getKeystorePath())
|
||||
|
||||
// Act
|
||||
boolean keyPasswordIsValid = KeyStoreUtils.isKeyPasswordCorrect(KEYSTORE_FILE.toURI().toURL(), KEYSTORE_TYPE, KEYSTORE_PASSWORD.toCharArray(), KEYSTORE_PASSWORD.toCharArray())
|
||||
boolean keyPasswordIsValid = KeyStoreUtils.isKeyPasswordCorrect(ksUrl, DEFAULT_STORE_TYPE, TEST_KEYSTORE_PASSWORD.toCharArray(), TEST_KEY_PASSWORD.toCharArray())
|
||||
|
||||
// Assert
|
||||
assert keyPasswordIsValid
|
||||
@ -94,9 +114,10 @@ class KeyStoreUtilsGroovyTest extends GroovyTestCase {
|
||||
@Test
|
||||
void testShouldVerifyKeyPasswordIsNotValid() {
|
||||
// Arrange
|
||||
final URL ksUrl = getKeystorePathAsUrl(tlsConfiguration.getKeystorePath())
|
||||
|
||||
// Act
|
||||
boolean keyPasswordIsValid = KeyStoreUtils.isKeyPasswordCorrect(KEYSTORE_FILE.toURI().toURL(), KEYSTORE_TYPE, KEYSTORE_PASSWORD.toCharArray(), KEYSTORE_PASSWORD.reverse().toCharArray())
|
||||
boolean keyPasswordIsValid = KeyStoreUtils.isKeyPasswordCorrect(ksUrl, tlsConfiguration.getKeystoreType(), TEST_KEYSTORE_PASSWORD.toCharArray(), TEST_KEY_PASSWORD.reverse().toCharArray())
|
||||
|
||||
// Assert
|
||||
assert !keyPasswordIsValid
|
||||
@ -141,4 +162,65 @@ class KeyStoreUtilsGroovyTest extends GroovyTestCase {
|
||||
FileOutputStream fos = new FileOutputStream("/Users/alopresto/Workspace/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/truststore.no-password.jks")
|
||||
truststore.store(fos, "".chars)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testShouldValidateTlsConfigAndNewKeystoreTruststoreWithParams() {
|
||||
// Assert
|
||||
assert tlsConfiguration.getKeystorePath()
|
||||
assert tlsConfiguration.getTruststorePath()
|
||||
assert tlsConfiguration.getKeystoreType() == DEFAULT_STORE_TYPE
|
||||
assert tlsConfiguration.getTruststoreType() == PKCS12_STORE_TYPE
|
||||
assert tlsConfiguration.getKeystorePassword() == TEST_KEYSTORE_PASSWORD
|
||||
}
|
||||
|
||||
@Test
|
||||
void testShouldValidateTlsConfigAndNewKeystoreTruststoreWithoutParams() {
|
||||
// Act
|
||||
TlsConfiguration testTlsConfig = KeyStoreUtils.createTlsConfigAndNewKeystoreTruststore()
|
||||
deleteKeystoreTruststore(testTlsConfig)
|
||||
|
||||
// Assert
|
||||
assert testTlsConfig.getKeystorePath()
|
||||
assert testTlsConfig.getKeyPassword() == testTlsConfig.getKeystorePassword()
|
||||
assert testTlsConfig.getTruststorePassword()
|
||||
assert testTlsConfig.getKeystoreType() == PKCS12_STORE_TYPE
|
||||
assert testTlsConfig.getTruststoreType() == PKCS12_STORE_TYPE
|
||||
}
|
||||
|
||||
@Test
|
||||
void testShouldValidateTlsConfigWithoutKeyPasswordParam() {
|
||||
// Arrange
|
||||
TlsConfiguration testTlsConfigParam = new StandardTlsConfiguration(null, TEST_KEYSTORE_PASSWORD, null, DEFAULT_STORE_TYPE, null, TEST_TRUSTSTORE_PASSWORD, DEFAULT_STORE_TYPE)
|
||||
|
||||
// Act
|
||||
final TlsConfiguration testTlsConfig = KeyStoreUtils.createTlsConfigAndNewKeystoreTruststore(testTlsConfigParam)
|
||||
deleteKeystoreTruststore(testTlsConfig)
|
||||
|
||||
// Assert
|
||||
assert testTlsConfig.getKeyPassword() == testTlsConfig.getKeystorePassword()
|
||||
}
|
||||
|
||||
private static URL getKeystorePathAsUrl(String path) {
|
||||
return new File(path).toURI().toURL()
|
||||
}
|
||||
|
||||
private static void deleteKeystoreTruststore(TlsConfiguration tlsConfig) {
|
||||
if (tlsConfig != null) {
|
||||
try {
|
||||
if (StringUtils.isNotBlank(tlsConfig.getKeystorePath())) {
|
||||
Files.deleteIfExists(Paths.get(tlsConfig.getKeystorePath()))
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new IOException("There was an error deleting a keystore: ${e.getMessage()}, ${e}");
|
||||
}
|
||||
|
||||
try {
|
||||
if (StringUtils.isNotBlank(tlsConfig.getTruststorePath())) {
|
||||
Files.deleteIfExists(Paths.get(tlsConfig.getTruststorePath()))
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new IOException("There was an error deleting a truststore: ${e.getMessage()}, ${e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,9 +17,6 @@
|
||||
|
||||
package org.apache.nifi.security.util;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
@ -31,6 +28,8 @@ import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
@ -16,30 +16,27 @@
|
||||
*/
|
||||
package org.apache.nifi.processors.standard;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.nifi.processors.standard.util.TestInvokeHttpCommon;
|
||||
import org.apache.nifi.security.util.KeystoreType;
|
||||
import org.apache.nifi.security.util.KeyStoreUtils;
|
||||
import org.apache.nifi.security.util.SslContextFactory;
|
||||
import org.apache.nifi.security.util.StandardTlsConfiguration;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.apache.nifi.ssl.SSLContextService;
|
||||
import org.apache.nifi.util.MockFlowFile;
|
||||
import org.apache.nifi.util.TestRunners;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
@ -48,30 +45,45 @@ import org.mockito.Mockito;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class TestInvokeHTTP extends TestInvokeHttpCommon {
|
||||
private static final Logger logger = LoggerFactory.getLogger(TestInvokeHTTP.class);
|
||||
|
||||
private static final String TRUSTSTORE_PATH = "src/test/resources/truststore.jks";
|
||||
private static final String TRUSTSTORE_PASSWORD = "passwordpassword";
|
||||
private static final KeystoreType TRUSTSTORE_TYPE = KeystoreType.JKS;
|
||||
private static final String KEYSTORE_PATH = "src/test/resources/keystore.jks";
|
||||
private static final String KEYSTORE_PASSWORD = "passwordpassword";
|
||||
private static final KeystoreType KEYSTORE_TYPE = KeystoreType.JKS;
|
||||
|
||||
private static final TlsConfiguration TLS_CONFIGURATION = new StandardTlsConfiguration(
|
||||
KEYSTORE_PATH,
|
||||
KEYSTORE_PASSWORD,
|
||||
KEYSTORE_TYPE,
|
||||
TRUSTSTORE_PATH,
|
||||
TRUSTSTORE_PASSWORD,
|
||||
TRUSTSTORE_TYPE
|
||||
);
|
||||
private static TlsConfiguration tlsConfiguration;
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
// generate new keystore and truststore
|
||||
tlsConfiguration = KeyStoreUtils.createTlsConfigAndNewKeystoreTruststore();
|
||||
configureServer(null, null);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() throws Exception {
|
||||
if (tlsConfiguration != null) {
|
||||
try {
|
||||
if (StringUtils.isNotBlank(tlsConfiguration.getKeystorePath())) {
|
||||
Files.deleteIfExists(Paths.get(tlsConfiguration.getKeystorePath()));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new IOException("There was an error deleting a keystore: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
try {
|
||||
if (StringUtils.isNotBlank(tlsConfiguration.getTruststorePath())) {
|
||||
Files.deleteIfExists(Paths.get(tlsConfiguration.getTruststorePath()));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new IOException("There was an error deleting a truststore: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void before() throws Exception {
|
||||
runner = TestRunners.newTestRunner(InvokeHTTP.class);
|
||||
@ -82,9 +94,9 @@ public class TestInvokeHTTP extends TestInvokeHttpCommon {
|
||||
final String serviceIdentifier = SSLContextService.class.getName();
|
||||
final SSLContextService sslContextService = Mockito.mock(SSLContextService.class);
|
||||
Mockito.when(sslContextService.getIdentifier()).thenReturn(serviceIdentifier);
|
||||
final SSLContext sslContext = SslContextFactory.createSslContext(TLS_CONFIGURATION);
|
||||
final SSLContext sslContext = SslContextFactory.createSslContext(tlsConfiguration);
|
||||
Mockito.when(sslContextService.createContext()).thenReturn(sslContext);
|
||||
Mockito.when(sslContextService.createTlsConfiguration()).thenReturn(TLS_CONFIGURATION);
|
||||
Mockito.when(sslContextService.createTlsConfiguration()).thenReturn(tlsConfiguration);
|
||||
|
||||
runner = TestRunners.newTestRunner(InvokeHTTP.class);
|
||||
|
||||
@ -140,20 +152,20 @@ public class TestInvokeHTTP extends TestInvokeHttpCommon {
|
||||
runner.setProperty(InvokeHTTP.PROP_URL, "http://nifi.apache.org/"); // just a dummy URL no connection goes out
|
||||
runner.setProperty(InvokeHTTP.PROP_PROXY_HOST, "${proxy.host}");
|
||||
|
||||
try{
|
||||
try {
|
||||
runner.run();
|
||||
Assert.fail();
|
||||
} catch (AssertionError e){
|
||||
} catch (AssertionError e) {
|
||||
// Expect assertion error when proxy port isn't set but host is.
|
||||
}
|
||||
runner.setProperty(InvokeHTTP.PROP_PROXY_PORT, "${proxy.port}");
|
||||
|
||||
runner.setProperty(InvokeHTTP.PROP_PROXY_USER, "${proxy.username}");
|
||||
|
||||
try{
|
||||
try {
|
||||
runner.run();
|
||||
Assert.fail();
|
||||
} catch (AssertionError e){
|
||||
} catch (AssertionError e) {
|
||||
// Expect assertion error when proxy password isn't set but host is.
|
||||
}
|
||||
runner.setProperty(InvokeHTTP.PROP_PROXY_PASSWORD, "${proxy.password}");
|
||||
@ -433,5 +445,4 @@ public class TestInvokeHTTP extends TestInvokeHttpCommon {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,63 +17,76 @@
|
||||
|
||||
package org.apache.nifi.processors.standard;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.nifi.processors.standard.util.TestInvokeHttpCommon;
|
||||
import org.apache.nifi.security.util.ClientAuth;
|
||||
import org.apache.nifi.security.util.KeystoreType;
|
||||
import org.apache.nifi.security.util.KeyStoreUtils;
|
||||
import org.apache.nifi.security.util.SslContextFactory;
|
||||
import org.apache.nifi.security.util.StandardTlsConfiguration;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
|
||||
import org.apache.nifi.ssl.SSLContextService;
|
||||
import org.apache.nifi.util.TestRunners;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
/**
|
||||
* Executes the same tests as TestInvokeHttp but with one-way SSL enabled. The Jetty server created for these tests
|
||||
* will not require client certificates and the client will not use keystore properties in the SSLContextService.
|
||||
*/
|
||||
public class TestInvokeHttpSSL extends TestInvokeHttpCommon {
|
||||
|
||||
protected static final String TRUSTSTORE_PATH = "src/test/resources/truststore.no-password.jks";
|
||||
protected static final String TRUSTSTORE_PASSWORD = "";
|
||||
protected static final KeystoreType TRUSTSTORE_TYPE = KeystoreType.JKS;
|
||||
|
||||
private static final String KEYSTORE_PATH = "src/test/resources/keystore.jks";
|
||||
private static final String KEYSTORE_PASSWORD = "passwordpassword";
|
||||
private static final KeystoreType KEYSTORE_TYPE = KeystoreType.JKS;
|
||||
|
||||
private static final String HTTP_CONNECT_TIMEOUT = "30 s";
|
||||
private static final String HTTP_READ_TIMEOUT = "30 s";
|
||||
|
||||
protected static final TlsConfiguration SERVER_CONFIGURATION = new StandardTlsConfiguration(
|
||||
KEYSTORE_PATH,
|
||||
KEYSTORE_PASSWORD,
|
||||
KEYSTORE_TYPE,
|
||||
TRUSTSTORE_PATH,
|
||||
TRUSTSTORE_PASSWORD,
|
||||
TRUSTSTORE_TYPE
|
||||
);
|
||||
protected static TlsConfiguration serverConfiguration;
|
||||
|
||||
protected static SSLContext clientSslContext;
|
||||
|
||||
private static final TlsConfiguration CLIENT_CONFIGURATION = new StandardTlsConfiguration(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
TRUSTSTORE_PATH,
|
||||
TRUSTSTORE_PASSWORD,
|
||||
TRUSTSTORE_TYPE
|
||||
);
|
||||
private static SSLContext truststoreSslContext;
|
||||
private static TlsConfiguration truststoreConfiguration;
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
final SSLContext serverContext = SslContextFactory.createSslContext(SERVER_CONFIGURATION);
|
||||
// generate new keystore and truststore
|
||||
serverConfiguration = KeyStoreUtils.createTlsConfigAndNewKeystoreTruststore();
|
||||
|
||||
truststoreConfiguration = new StandardTlsConfiguration(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
serverConfiguration.getTruststorePath(),
|
||||
serverConfiguration.getTruststorePassword(),
|
||||
serverConfiguration.getTruststoreType()
|
||||
);
|
||||
|
||||
final SSLContext serverContext = SslContextFactory.createSslContext(serverConfiguration);
|
||||
configureServer(serverContext, ClientAuth.NONE);
|
||||
clientSslContext = SslContextFactory.createSslContext(CLIENT_CONFIGURATION);
|
||||
truststoreSslContext = SslContextFactory.createSslContext(truststoreConfiguration);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() throws Exception {
|
||||
if (serverConfiguration != null) {
|
||||
try {
|
||||
if (StringUtils.isNotBlank(serverConfiguration.getKeystorePath())) {
|
||||
Files.deleteIfExists(Paths.get(serverConfiguration.getKeystorePath()));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new IOException("There was an error deleting a keystore: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
try {
|
||||
if (StringUtils.isNotBlank(serverConfiguration.getTruststorePath())) {
|
||||
Files.deleteIfExists(Paths.get(serverConfiguration.getTruststorePath()));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new IOException("There was an error deleting a truststore: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
@ -82,8 +95,8 @@ public class TestInvokeHttpSSL extends TestInvokeHttpCommon {
|
||||
final String serviceIdentifier = SSLContextService.class.getName();
|
||||
|
||||
Mockito.when(sslContextService.getIdentifier()).thenReturn(serviceIdentifier);
|
||||
Mockito.when(sslContextService.createContext()).thenReturn(clientSslContext);
|
||||
Mockito.when(sslContextService.createTlsConfiguration()).thenReturn(CLIENT_CONFIGURATION);
|
||||
Mockito.when(sslContextService.createContext()).thenReturn(getClientSslContext());
|
||||
Mockito.when(sslContextService.createTlsConfiguration()).thenReturn(getClientConfiguration());
|
||||
|
||||
runner = TestRunners.newTestRunner(InvokeHTTP.class);
|
||||
runner.addControllerService(serviceIdentifier, sslContextService);
|
||||
@ -93,4 +106,12 @@ public class TestInvokeHttpSSL extends TestInvokeHttpCommon {
|
||||
runner.setProperty(InvokeHTTP.PROP_CONNECT_TIMEOUT, HTTP_CONNECT_TIMEOUT);
|
||||
runner.setProperty(InvokeHTTP.PROP_READ_TIMEOUT, HTTP_READ_TIMEOUT);
|
||||
}
|
||||
|
||||
protected SSLContext getClientSslContext() {
|
||||
return truststoreSslContext;
|
||||
}
|
||||
|
||||
protected TlsConfiguration getClientConfiguration() {
|
||||
return truststoreConfiguration;
|
||||
}
|
||||
}
|
||||
|
@ -17,14 +17,17 @@
|
||||
|
||||
package org.apache.nifi.processors.standard;
|
||||
|
||||
import org.apache.nifi.security.util.ClientAuth;
|
||||
import org.apache.nifi.security.util.KeystoreType;
|
||||
import org.apache.nifi.security.util.SslContextFactory;
|
||||
import org.apache.nifi.security.util.StandardTlsConfiguration;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.junit.BeforeClass;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.nifi.security.util.ClientAuth;
|
||||
import org.apache.nifi.security.util.KeyStoreUtils;
|
||||
import org.apache.nifi.security.util.SslContextFactory;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
|
||||
/**
|
||||
* This is probably overkill but in keeping with the same pattern as the TestInvokeHttp and TestInvokeHttpSSL class,
|
||||
@ -33,23 +36,48 @@ import javax.net.ssl.SSLContext;
|
||||
*/
|
||||
public class TestInvokeHttpTwoWaySSL extends TestInvokeHttpSSL {
|
||||
|
||||
private static final String CLIENT_KEYSTORE_PATH = "src/test/resources/client-keystore.p12";
|
||||
private static final String CLIENT_KEYSTORE_PASSWORD = "passwordpassword";
|
||||
private static final KeystoreType CLIENT_KEYSTORE_TYPE = KeystoreType.PKCS12;
|
||||
|
||||
private static final TlsConfiguration CLIENT_CONFIGURATION = new StandardTlsConfiguration(
|
||||
CLIENT_KEYSTORE_PATH,
|
||||
CLIENT_KEYSTORE_PASSWORD,
|
||||
CLIENT_KEYSTORE_TYPE,
|
||||
TRUSTSTORE_PATH,
|
||||
TRUSTSTORE_PASSWORD,
|
||||
TRUSTSTORE_TYPE
|
||||
);
|
||||
private static TlsConfiguration serverConfig;
|
||||
private static SSLContext clientSslContext;
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
final SSLContext serverContext = SslContextFactory.createSslContext(SERVER_CONFIGURATION);
|
||||
// generate new keystore and truststore
|
||||
serverConfig = KeyStoreUtils.createTlsConfigAndNewKeystoreTruststore();
|
||||
|
||||
final SSLContext serverContext = SslContextFactory.createSslContext(serverConfig);
|
||||
configureServer(serverContext, ClientAuth.REQUIRED);
|
||||
clientSslContext = SslContextFactory.createSslContext(CLIENT_CONFIGURATION);
|
||||
clientSslContext = SslContextFactory.createSslContext(serverConfig);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() throws Exception {
|
||||
if (serverConfig != null) {
|
||||
try {
|
||||
if (StringUtils.isNotBlank(serverConfig.getKeystorePath())) {
|
||||
Files.deleteIfExists(Paths.get(serverConfig.getKeystorePath()));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new IOException("There was an error deleting a keystore: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
try {
|
||||
if (StringUtils.isNotBlank(serverConfig.getTruststorePath())) {
|
||||
Files.deleteIfExists(Paths.get(serverConfig.getTruststorePath()));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new IOException("There was an error deleting a truststore: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SSLContext getClientSslContext() {
|
||||
return clientSslContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TlsConfiguration getClientConfiguration() {
|
||||
return serverConfig;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -16,23 +16,23 @@
|
||||
*/
|
||||
package org.apache.nifi.processors.standard;
|
||||
|
||||
import static org.apache.nifi.processors.standard.ListenHTTP.RELATIONSHIP_SUCCESS;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.io.Files;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -51,22 +51,24 @@ import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.nifi.processor.ProcessContext;
|
||||
import org.apache.nifi.processor.ProcessSessionFactory;
|
||||
import org.apache.nifi.remote.io.socket.NetworkUtils;
|
||||
import org.apache.nifi.reporting.InitializationException;
|
||||
import org.apache.nifi.security.util.KeyStoreUtils;
|
||||
import org.apache.nifi.security.util.KeystoreType;
|
||||
import org.apache.nifi.security.util.SslContextFactory;
|
||||
import org.apache.nifi.security.util.StandardTlsConfiguration;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.apache.nifi.security.util.TlsException;
|
||||
import org.apache.nifi.ssl.RestrictedSSLContextService;
|
||||
import org.apache.nifi.ssl.SSLContextService;
|
||||
import org.apache.nifi.util.MockFlowFile;
|
||||
import org.apache.nifi.util.TestRunner;
|
||||
import org.apache.nifi.util.TestRunners;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
@ -74,6 +76,11 @@ import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import static org.apache.nifi.processors.standard.ListenHTTP.RELATIONSHIP_SUCCESS;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class TestListenHTTP {
|
||||
|
||||
private static final String SSL_CONTEXT_SERVICE_IDENTIFIER = "ssl-context";
|
||||
@ -105,39 +112,12 @@ public class TestListenHTTP {
|
||||
private static final int SOCKET_CONNECT_TIMEOUT = 100;
|
||||
private static final long SERVER_START_TIMEOUT = 1200000;
|
||||
|
||||
private static final TlsConfiguration SERVER_CONFIGURATION = new StandardTlsConfiguration(
|
||||
KEYSTORE,
|
||||
KEYSTORE_PASSWORD,
|
||||
KEYSTORE_PASSWORD,
|
||||
KEYSTORE_TYPE,
|
||||
TRUSTSTORE,
|
||||
TRUSTSTORE_PASSWORD,
|
||||
TRUSTSTORE_TYPE,
|
||||
TLS_1_2
|
||||
);
|
||||
private static final TlsConfiguration SERVER_TLS_1_3_CONFIGURATION = new StandardTlsConfiguration(
|
||||
KEYSTORE,
|
||||
KEYSTORE_PASSWORD,
|
||||
KEYSTORE_PASSWORD,
|
||||
KEYSTORE_TYPE,
|
||||
TRUSTSTORE,
|
||||
TRUSTSTORE_PASSWORD,
|
||||
TRUSTSTORE_TYPE,
|
||||
TLS_1_3
|
||||
);
|
||||
private static final TlsConfiguration SERVER_NO_TRUSTSTORE_CONFIGURATION = new StandardTlsConfiguration(
|
||||
KEYSTORE,
|
||||
KEYSTORE_PASSWORD,
|
||||
KEYSTORE_PASSWORD,
|
||||
KEYSTORE_TYPE,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
TLS_1_2
|
||||
);
|
||||
private static TlsConfiguration tlsConfiguration;
|
||||
private static TlsConfiguration serverConfiguration;
|
||||
private static TlsConfiguration serverTls_1_3_Configuration;
|
||||
private static TlsConfiguration serverNoTruststoreConfiguration;
|
||||
private static SSLContext serverKeyStoreSslContext;
|
||||
private static SSLContext serverKeyStoreNoTrustStoreSslContext;
|
||||
|
||||
private static SSLContext keyStoreSslContext;
|
||||
private static SSLContext trustStoreSslContext;
|
||||
|
||||
@ -147,29 +127,84 @@ public class TestListenHTTP {
|
||||
private int availablePort;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpSuite() throws TlsException {
|
||||
serverKeyStoreSslContext = SslContextFactory.createSslContext(SERVER_CONFIGURATION);
|
||||
final TrustManager[] defaultTrustManagers = SslContextFactory.getTrustManagers(SERVER_NO_TRUSTSTORE_CONFIGURATION);
|
||||
serverKeyStoreNoTrustStoreSslContext = SslContextFactory.createSslContext(SERVER_NO_TRUSTSTORE_CONFIGURATION, defaultTrustManagers);
|
||||
public static void setUpSuite() throws GeneralSecurityException, IOException {
|
||||
// generate new keystore and truststore
|
||||
tlsConfiguration = KeyStoreUtils.createTlsConfigAndNewKeystoreTruststore();
|
||||
|
||||
serverConfiguration = new StandardTlsConfiguration(
|
||||
tlsConfiguration.getKeystorePath(),
|
||||
tlsConfiguration.getKeystorePassword(),
|
||||
tlsConfiguration.getKeyPassword(),
|
||||
tlsConfiguration.getKeystoreType(),
|
||||
tlsConfiguration.getTruststorePath(),
|
||||
tlsConfiguration.getTruststorePassword(),
|
||||
tlsConfiguration.getTruststoreType(),
|
||||
TLS_1_2
|
||||
);
|
||||
serverTls_1_3_Configuration = new StandardTlsConfiguration(
|
||||
tlsConfiguration.getKeystorePath(),
|
||||
tlsConfiguration.getKeystorePassword(),
|
||||
tlsConfiguration.getKeyPassword(),
|
||||
tlsConfiguration.getKeystoreType(),
|
||||
tlsConfiguration.getTruststorePath(),
|
||||
tlsConfiguration.getTruststorePassword(),
|
||||
tlsConfiguration.getTruststoreType(),
|
||||
TLS_1_3
|
||||
);
|
||||
serverNoTruststoreConfiguration = new StandardTlsConfiguration(
|
||||
tlsConfiguration.getKeystorePath(),
|
||||
tlsConfiguration.getKeystorePassword(),
|
||||
tlsConfiguration.getKeyPassword(),
|
||||
tlsConfiguration.getKeystoreType(),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
TLS_1_2
|
||||
);
|
||||
|
||||
serverKeyStoreSslContext = SslContextFactory.createSslContext(serverConfiguration);
|
||||
final TrustManager[] defaultTrustManagers = SslContextFactory.getTrustManagers(serverNoTruststoreConfiguration);
|
||||
serverKeyStoreNoTrustStoreSslContext = SslContextFactory.createSslContext(serverNoTruststoreConfiguration, defaultTrustManagers);
|
||||
|
||||
keyStoreSslContext = SslContextFactory.createSslContext(new StandardTlsConfiguration(
|
||||
CLIENT_KEYSTORE,
|
||||
KEYSTORE_PASSWORD,
|
||||
CLIENT_KEYSTORE_TYPE,
|
||||
TRUSTSTORE,
|
||||
TRUSTSTORE_PASSWORD,
|
||||
TRUSTSTORE_TYPE)
|
||||
tlsConfiguration.getKeystorePath(),
|
||||
tlsConfiguration.getKeystorePassword(),
|
||||
tlsConfiguration.getKeystoreType(),
|
||||
tlsConfiguration.getTruststorePath(),
|
||||
tlsConfiguration.getTruststorePassword(),
|
||||
tlsConfiguration.getTruststoreType())
|
||||
);
|
||||
trustStoreSslContext = SslContextFactory.createSslContext(new StandardTlsConfiguration(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
TRUSTSTORE,
|
||||
TRUSTSTORE_PASSWORD,
|
||||
TRUSTSTORE_TYPE)
|
||||
tlsConfiguration.getTruststorePath(),
|
||||
tlsConfiguration.getTruststorePassword(),
|
||||
tlsConfiguration.getTruststoreType())
|
||||
);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() throws Exception {
|
||||
if (tlsConfiguration != null) {
|
||||
try {
|
||||
if (StringUtils.isNotBlank(tlsConfiguration.getKeystorePath())) {
|
||||
Files.deleteIfExists(Paths.get(tlsConfiguration.getKeystorePath()));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new IOException("There was an error deleting a keystore: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
try {
|
||||
if (StringUtils.isNotBlank(tlsConfiguration.getTruststorePath())) {
|
||||
Files.deleteIfExists(Paths.get(tlsConfiguration.getTruststorePath()));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new IOException("There was an error deleting a truststore: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() throws IOException {
|
||||
proc = new ListenHTTP();
|
||||
@ -223,7 +258,7 @@ public class TestListenHTTP {
|
||||
|
||||
@Test
|
||||
public void testSecurePOSTRequestsReceivedWithoutEL() throws Exception {
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, SERVER_NO_TRUSTSTORE_CONFIGURATION);
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, serverNoTruststoreConfiguration);
|
||||
|
||||
runner.setProperty(ListenHTTP.PORT, Integer.toString(availablePort));
|
||||
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_BASE_PATH);
|
||||
@ -234,7 +269,7 @@ public class TestListenHTTP {
|
||||
|
||||
@Test
|
||||
public void testSecurePOSTRequestsReturnCodeReceivedWithoutEL() throws Exception {
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, SERVER_NO_TRUSTSTORE_CONFIGURATION);
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, serverNoTruststoreConfiguration);
|
||||
|
||||
runner.setProperty(ListenHTTP.PORT, Integer.toString(availablePort));
|
||||
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_BASE_PATH);
|
||||
@ -246,7 +281,7 @@ public class TestListenHTTP {
|
||||
|
||||
@Test
|
||||
public void testSecurePOSTRequestsReceivedWithEL() throws Exception {
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, SERVER_NO_TRUSTSTORE_CONFIGURATION);
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, serverNoTruststoreConfiguration);
|
||||
|
||||
runner.setProperty(ListenHTTP.PORT, HTTP_SERVER_PORT_EL);
|
||||
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_SERVER_BASEPATH_EL);
|
||||
@ -257,7 +292,7 @@ public class TestListenHTTP {
|
||||
|
||||
@Test
|
||||
public void testSecurePOSTRequestsReturnCodeReceivedWithEL() throws Exception {
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, SERVER_NO_TRUSTSTORE_CONFIGURATION);
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, serverNoTruststoreConfiguration);
|
||||
|
||||
runner.setProperty(ListenHTTP.PORT, Integer.toString(availablePort));
|
||||
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_BASE_PATH);
|
||||
@ -269,7 +304,7 @@ public class TestListenHTTP {
|
||||
|
||||
@Test
|
||||
public void testSecureTwoWaySslPOSTRequestsReceivedWithoutEL() throws Exception {
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.REQUIRED, SERVER_CONFIGURATION);
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.REQUIRED, serverConfiguration);
|
||||
|
||||
runner.setProperty(ListenHTTP.PORT, Integer.toString(availablePort));
|
||||
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_BASE_PATH);
|
||||
@ -280,7 +315,7 @@ public class TestListenHTTP {
|
||||
|
||||
@Test
|
||||
public void testSecureTwoWaySslPOSTRequestsReturnCodeReceivedWithoutEL() throws Exception {
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.REQUIRED, SERVER_CONFIGURATION);
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.REQUIRED, serverConfiguration);
|
||||
|
||||
runner.setProperty(ListenHTTP.PORT, Integer.toString(availablePort));
|
||||
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_BASE_PATH);
|
||||
@ -292,7 +327,7 @@ public class TestListenHTTP {
|
||||
|
||||
@Test
|
||||
public void testSecureTwoWaySslPOSTRequestsReceivedWithEL() throws Exception {
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.REQUIRED, SERVER_CONFIGURATION);
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.REQUIRED, serverConfiguration);
|
||||
|
||||
runner.setProperty(ListenHTTP.PORT, HTTP_SERVER_PORT_EL);
|
||||
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_SERVER_BASEPATH_EL);
|
||||
@ -303,7 +338,7 @@ public class TestListenHTTP {
|
||||
|
||||
@Test
|
||||
public void testSecureTwoWaySslPOSTRequestsReturnCodeReceivedWithEL() throws Exception {
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.REQUIRED, SERVER_CONFIGURATION);
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.REQUIRED, serverConfiguration);
|
||||
|
||||
runner.setProperty(ListenHTTP.PORT, Integer.toString(availablePort));
|
||||
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_BASE_PATH);
|
||||
@ -315,12 +350,12 @@ public class TestListenHTTP {
|
||||
|
||||
@Test
|
||||
public void testSecureServerSupportsCurrentTlsProtocolVersion() throws Exception {
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, SERVER_NO_TRUSTSTORE_CONFIGURATION);
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, serverNoTruststoreConfiguration);
|
||||
startSecureServer();
|
||||
|
||||
final SSLSocketFactory sslSocketFactory = trustStoreSslContext.getSocketFactory();
|
||||
final SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(LOCALHOST, availablePort);
|
||||
final String currentProtocol = SERVER_NO_TRUSTSTORE_CONFIGURATION.getProtocol();
|
||||
final String currentProtocol = serverNoTruststoreConfiguration.getProtocol();
|
||||
sslSocket.setEnabledProtocols(new String[]{currentProtocol});
|
||||
|
||||
sslSocket.startHandshake();
|
||||
@ -330,7 +365,7 @@ public class TestListenHTTP {
|
||||
|
||||
@Test
|
||||
public void testSecureServerTrustStoreConfiguredClientAuthenticationRequired() throws Exception {
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.REQUIRED, SERVER_CONFIGURATION);
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.REQUIRED, serverConfiguration);
|
||||
startSecureServer();
|
||||
final HttpsURLConnection connection = getSecureConnection(trustStoreSslContext);
|
||||
assertThrows(SSLException.class, connection::getResponseCode);
|
||||
@ -342,7 +377,7 @@ public class TestListenHTTP {
|
||||
|
||||
@Test
|
||||
public void testSecureServerTrustStoreNotConfiguredClientAuthenticationNotRequired() throws Exception {
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, SERVER_NO_TRUSTSTORE_CONFIGURATION);
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, serverNoTruststoreConfiguration);
|
||||
startSecureServer();
|
||||
final HttpsURLConnection connection = getSecureConnection(trustStoreSslContext);
|
||||
final int responseCode = connection.getResponseCode();
|
||||
@ -355,7 +390,7 @@ public class TestListenHTTP {
|
||||
final String protocolMessage = String.format("TLS Protocol required [%s] found [%s]", TLS_1_3, currentProtocol);
|
||||
Assume.assumeTrue(protocolMessage, TLS_1_3.equals(currentProtocol));
|
||||
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, SERVER_TLS_1_3_CONFIGURATION);
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, serverTls_1_3_Configuration);
|
||||
|
||||
runner.setProperty(ListenHTTP.PORT, Integer.toString(availablePort));
|
||||
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_BASE_PATH);
|
||||
@ -541,11 +576,14 @@ public class TestListenHTTP {
|
||||
|
||||
Runnable sendRequestToWebserver = () -> {
|
||||
try {
|
||||
File file1 = createTextFile("my-file-text-", ".txt", "Hello", "World");
|
||||
File file2 = createTextFile("my-file-text-", ".txt", "{ \"name\":\"John\", \"age\":30 }");
|
||||
|
||||
MultipartBody multipartBody = new MultipartBody.Builder().setType(MultipartBody.FORM)
|
||||
.addFormDataPart("p1", "v1")
|
||||
.addFormDataPart("p2", "v2")
|
||||
.addFormDataPart("file1", "my-file-text.txt", RequestBody.create(MediaType.parse("text/plain"), createTextFile("my-file-text.txt", "Hello", "World")))
|
||||
.addFormDataPart("file2", "my-file-data.json", RequestBody.create(MediaType.parse("application/json"), createTextFile("my-file-text.txt", "{ \"name\":\"John\", \"age\":30 }")))
|
||||
.addFormDataPart("file1", "my-file-text.txt", RequestBody.create(MediaType.parse("text/plain"), file1))
|
||||
.addFormDataPart("file2", "my-file-data.json", RequestBody.create(MediaType.parse("application/json"), file2))
|
||||
.addFormDataPart("file3", "my-file-binary.bin", RequestBody.create(MediaType.parse("application/octet-stream"), generateRandomBinaryData(100)))
|
||||
.build();
|
||||
|
||||
@ -562,6 +600,8 @@ public class TestListenHTTP {
|
||||
.build();
|
||||
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
Files.deleteIfExists(Paths.get(String.valueOf(file1)));
|
||||
Files.deleteIfExists(Paths.get(String.valueOf(file2)));
|
||||
Assert.assertTrue(String.format("Unexpected code: %s, body: %s", response.code(), response.body().string()), response.isSuccessful());
|
||||
}
|
||||
} catch (final Throwable t) {
|
||||
@ -625,13 +665,12 @@ public class TestListenHTTP {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private File createTextFile(String fileName, String... lines) throws IOException {
|
||||
File file = new File("target/" + fileName);
|
||||
file.deleteOnExit();
|
||||
for (String string : lines) {
|
||||
Files.append(string, file, Charsets.UTF_8);
|
||||
private File createTextFile(String prefix, String extension, String...lines) throws IOException {
|
||||
Path file = Files.createTempFile(prefix, extension);
|
||||
try (FileOutputStream fos = new FileOutputStream(file.toFile())) {
|
||||
IOUtils.writeLines(Arrays.asList(lines), System.lineSeparator(), fos, Charsets.UTF_8);
|
||||
}
|
||||
return file;
|
||||
return file.toFile();
|
||||
}
|
||||
|
||||
protected MockFlowFile findFlowFile(List<MockFlowFile> flowFilesForRelationship, String attributeName, String attributeValue) {
|
||||
|
@ -17,12 +17,6 @@
|
||||
|
||||
package org.apache.nifi.processors.standard.util;
|
||||
|
||||
import static org.apache.commons.codec.binary.Base64.encodeBase64;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
@ -39,7 +33,6 @@ import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.Part;
|
||||
|
||||
import org.apache.nifi.components.PropertyDescriptor;
|
||||
import org.apache.nifi.expression.ExpressionLanguageScope;
|
||||
import org.apache.nifi.flowfile.attributes.CoreAttributes;
|
||||
@ -70,6 +63,12 @@ import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.apache.commons.codec.binary.Base64.encodeBase64;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public abstract class TestInvokeHttpCommon {
|
||||
|
||||
protected static Server server;
|
||||
|
Loading…
x
Reference in New Issue
Block a user