From 0de89452f1975c5724e75e5f9d5865bb814cf4de Mon Sep 17 00:00:00 2001 From: Troy Melhase Date: Mon, 10 Feb 2020 11:56:09 -0900 Subject: [PATCH] NIFI-6927 Consolidate SSL context and trust managers for OkHttp on JDK9. Fixes name conflicts. This closes #4047. Signed-off-by: Andy LoPresto --- .../http/HttpNotificationService.java | 69 ++++---------- .../nifi/security/util/SslContextFactory.java | 71 ++++++++++---- .../nifi-elasticsearch-processors/pom.xml | 2 +- .../AbstractElasticsearchHttpProcessor.java | 31 ++++++- .../TestQueryElasticsearchHttpNoHits.java | 2 +- .../okhttp/OkHttpReplicationClient.java | 84 ++++++----------- .../security/util/SslServerSocketFactory.java | 82 ----------------- .../security/util/SslSocketFactory.java | 92 ------------------- .../nifi/processors/standard/InvokeHTTP.java | 20 +++- .../apache/nifi/lookup/RestLookupService.java | 38 +++++++- 10 files changed, 184 insertions(+), 307 deletions(-) delete mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-security/src/main/java/org/apache/nifi/framework/security/util/SslServerSocketFactory.java delete mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-security/src/main/java/org/apache/nifi/framework/security/util/SslSocketFactory.java diff --git a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/http/HttpNotificationService.java b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/http/HttpNotificationService.java index 1405625e7c..2ab7c72ffc 100644 --- a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/http/HttpNotificationService.java +++ b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/http/HttpNotificationService.java @@ -28,16 +28,13 @@ import org.apache.nifi.bootstrap.notification.NotificationFailedException; import org.apache.nifi.bootstrap.notification.NotificationInitializationContext; import org.apache.nifi.bootstrap.notification.NotificationType; import org.apache.nifi.components.PropertyDescriptor; -import org.apache.nifi.components.PropertyValue; import org.apache.nifi.expression.AttributeExpression; import org.apache.nifi.expression.ExpressionLanguageScope; -import org.apache.nifi.processor.exception.ProcessException; import org.apache.nifi.processor.util.StandardValidators; import org.apache.nifi.security.util.SslContextFactory; import org.apache.nifi.util.Tuple; import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.util.ArrayList; @@ -196,12 +193,25 @@ public class HttpNotificationService extends AbstractNotificationService { // check if the keystore is set and add the factory if so if (url.toLowerCase().startsWith("https")) { try { - Tuple sslSocketFactoryWithTrustManagers = getSslSocketFactory(context); + Tuple sslContextTuple = SslContextFactory.createTrustSslContextWithTrustManagers( + context.getProperty(HttpNotificationService.PROP_KEYSTORE).getValue(), + context.getProperty(HttpNotificationService.PROP_KEYSTORE_PASSWORD).isSet() + ? context.getProperty(HttpNotificationService.PROP_KEYSTORE_PASSWORD).getValue().toCharArray() : null, + context.getProperty(HttpNotificationService.PROP_KEY_PASSWORD).isSet() + ? context.getProperty(HttpNotificationService.PROP_KEY_PASSWORD).getValue().toCharArray() : null, + context.getProperty(HttpNotificationService.PROP_KEYSTORE_TYPE).getValue(), + context.getProperty(HttpNotificationService.PROP_TRUSTSTORE).getValue(), + context.getProperty(HttpNotificationService.PROP_TRUSTSTORE_PASSWORD).isSet() + ? context.getProperty(HttpNotificationService.PROP_TRUSTSTORE_PASSWORD).getValue().toCharArray() : null, + context.getProperty(HttpNotificationService.PROP_TRUSTSTORE_TYPE).getValue(), + SslContextFactory.ClientAuth.REQUIRED, + context.getProperty(HttpNotificationService.SSL_ALGORITHM).getValue() + ); // Find the first X509TrustManager - List x509TrustManagers = Arrays.stream(sslSocketFactoryWithTrustManagers.getValue()) + List x509TrustManagers = Arrays.stream(sslContextTuple.getValue()) .filter(trustManager -> trustManager instanceof X509TrustManager) .map(trustManager -> (X509TrustManager) trustManager).collect(Collectors.toList()); - okHttpClientBuilder.sslSocketFactory(sslSocketFactoryWithTrustManagers.getKey(), x509TrustManagers.get(0)); + okHttpClientBuilder.sslSocketFactory(sslContextTuple.getKey().getSocketFactory(), x509TrustManagers.get(0)); } catch (Exception e) { throw new IllegalStateException(e); } @@ -249,51 +259,4 @@ public class HttpNotificationService extends AbstractNotificationService { throw new NotificationFailedException("Failed to send Http Notification", e); } } - - private static Tuple getSslSocketFactory(NotificationInitializationContext context) { - - final String protocol = context.getProperty(SSL_ALGORITHM).getValue(); - try { - final PropertyValue keyPasswdProp = context.getProperty(PROP_KEY_PASSWORD); - final char[] keyPassword = keyPasswdProp.isSet() ? keyPasswdProp.getValue().toCharArray() : null; - - final Tuple sslContextWithTrustManagers; - final String truststoreFile = context.getProperty(PROP_TRUSTSTORE).getValue(); - final String keystoreFile = context.getProperty(PROP_KEYSTORE).getValue(); - - if (keystoreFile == null) { - // If keystore not specified, create SSL Context based only on trust store. - sslContextWithTrustManagers = SslContextFactory.createTrustSslContextWithTrustManagers( - context.getProperty(PROP_TRUSTSTORE).getValue(), - context.getProperty(PROP_TRUSTSTORE_PASSWORD).getValue().toCharArray(), - context.getProperty(PROP_TRUSTSTORE_TYPE).getValue(), - protocol); - - } else if (truststoreFile == null) { - // If truststore not specified, create SSL Context based only on key store. - sslContextWithTrustManagers = SslContextFactory.createSslContextWithTrustManagers( - context.getProperty(PROP_KEYSTORE).getValue(), - context.getProperty(PROP_KEYSTORE_PASSWORD).getValue().toCharArray(), - keyPassword, - context.getProperty(PROP_KEYSTORE_TYPE).getValue(), - protocol); - - } else { - sslContextWithTrustManagers = SslContextFactory.createSslContextWithTrustManagers( - context.getProperty(PROP_KEYSTORE).getValue(), - context.getProperty(PROP_KEYSTORE_PASSWORD).getValue().toCharArray(), - keyPassword, - context.getProperty(PROP_KEYSTORE_TYPE).getValue(), - context.getProperty(PROP_TRUSTSTORE).getValue(), - context.getProperty(PROP_TRUSTSTORE_PASSWORD).getValue().toCharArray(), - context.getProperty(PROP_TRUSTSTORE_TYPE).getValue(), - SslContextFactory.ClientAuth.REQUIRED, - protocol); - } - - return new Tuple<>(sslContextWithTrustManagers.getKey().getSocketFactory(), sslContextWithTrustManagers.getValue()); - } catch (final Exception e) { - throw new ProcessException(e); - } - } } diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/SslContextFactory.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/SslContextFactory.java index 8863260adb..3e8058fce0 100644 --- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/SslContextFactory.java +++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/SslContextFactory.java @@ -50,7 +50,7 @@ public final class SslContextFactory { } /** - * Creates a SSLContext instance using the given information. The password for the key is assumed to be the same + * Creates an SSLContext instance using the given information. The password for the key is assumed to be the same * as the password for the keystore. If this is not the case, the {@link #createSslContext(String, char[], char[], String, String, char[], String, ClientAuth, String)} * method should be used instead * @@ -63,7 +63,7 @@ public final class SslContextFactory { * @param clientAuth the type of client authentication * @param protocol the protocol to use for the SSL connection * - * @return a SSLContext instance + * @return an SSLContext instance * @throws java.security.KeyStoreException if any issues accessing the keystore * @throws java.io.IOException for any problems loading the keystores * @throws java.security.NoSuchAlgorithmException if an algorithm is found to be used but is unknown @@ -83,10 +83,11 @@ public final class SslContextFactory { } /** - * Creates a SSLContext instance using the given information. + * Creates an SSLContext instance using the given information. * * @param keystore the full path to the keystore * @param keystorePasswd the keystore password + * @param keyPasswd the password for the key within the keystore * @param keystoreType the type of keystore (e.g., PKCS12, JKS) * @param truststore the full path to the truststore * @param truststorePasswd the truststore password @@ -94,7 +95,7 @@ public final class SslContextFactory { * @param clientAuth the type of client authentication * @param protocol the protocol to use for the SSL connection * - * @return a SSLContext instance + * @return an SSLContext instance * @throws java.security.KeyStoreException if any issues accessing the keystore * @throws java.io.IOException for any problems loading the keystores * @throws java.security.NoSuchAlgorithmException if an algorithm is found to be used but is unknown @@ -113,10 +114,11 @@ public final class SslContextFactory { } /** - * Creates a SSLContext instance paired with its TrustManager instances using the given information. + * Creates an SSLContext instance paired with its TrustManager instances using the given information. * * @param keystore the full path to the keystore * @param keystorePasswd the keystore password + * @param keyPasswd the password for the key within the keystore * @param keystoreType the type of keystore (e.g., PKCS12, JKS) * @param truststore the full path to the truststore * @param truststorePasswd the truststore password @@ -124,7 +126,7 @@ public final class SslContextFactory { * @param clientAuth the type of client authentication * @param protocol the protocol to use for the SSL connection * - * @return a {@link Tuple} pairing a SSLContext instance with its TrustManagers + * @return a {@link Tuple} pairing an SSLContext instance with its TrustManagers * @throws java.security.KeyStoreException if any issues accessing the keystore * @throws java.io.IOException for any problems loading the keystores * @throws java.security.NoSuchAlgorithmException if an algorithm is found to be used but is unknown @@ -174,7 +176,7 @@ public final class SslContextFactory { } /** - * Creates a SSLContext instance using the given information. This method assumes that the key password is + * Creates an SSLContext instance using the given information. This method assumes that the key password is * the same as the keystore password. If this is not the case, use the {@link #createSslContext(String, char[], char[], String, String)} * method instead. * @@ -183,7 +185,7 @@ public final class SslContextFactory { * @param keystoreType the type of keystore (e.g., PKCS12, JKS) * @param protocol the protocol to use for the SSL connection * - * @return a SSLContext instance + * @return an SSLContext instance * @throws java.security.KeyStoreException if any issues accessing the keystore * @throws java.io.IOException for any problems loading the keystores * @throws java.security.NoSuchAlgorithmException if an algorithm is found to be used but is unknown @@ -201,14 +203,15 @@ public final class SslContextFactory { } /** - * Creates a SSLContext instance using the given information. + * Creates an SSLContext instance using the given information. * * @param keystore the full path to the keystore * @param keystorePasswd the keystore password + * @param keyPasswd the password for the key within the keystore * @param keystoreType the type of keystore (e.g., PKCS12, JKS) * @param protocol the protocol to use for the SSL connection * - * @return a SSLContext instance + * @return an SSLContext instance * @throws java.security.KeyStoreException if any issues accessing the keystore * @throws java.io.IOException for any problems loading the keystores * @throws java.security.NoSuchAlgorithmException if an algorithm is found to be used but is unknown @@ -224,14 +227,15 @@ public final class SslContextFactory { } /** - * Creates a SSLContext instance paired with its TrustManager instances using the given information. + * Creates an SSLContext instance paired with its TrustManager instances using the given information. * * @param keystore the full path to the keystore * @param keystorePasswd the keystore password + * @param keyPasswd the password for the key within the keystore * @param keystoreType the type of keystore (e.g., PKCS12, JKS) * @param protocol the protocol to use for the SSL connection * - * @return a {@link Tuple} pairing a SSLContext instance paired with its TrustManager instances + * @return a {@link Tuple} pairing an SSLContext instance paired with its TrustManager instances * @throws java.security.KeyStoreException if any issues accessing the keystore * @throws java.io.IOException for any problems loading the keystores * @throws java.security.NoSuchAlgorithmException if an algorithm is found to be used but is unknown @@ -265,14 +269,14 @@ public final class SslContextFactory { } /** - * Creates a SSLContext instance using the given information. + * Creates an SSLContext instance using the given information. * * @param truststore the full path to the truststore * @param truststorePasswd the truststore password * @param truststoreType the type of truststore (e.g., PKCS12, JKS) * @param protocol the protocol to use for the SSL connection * - * @return a SSLContext instance + * @return an SSLContext instance * @throws java.security.KeyStoreException if any issues accessing the keystore * @throws java.io.IOException for any problems loading the keystores * @throws java.security.NoSuchAlgorithmException if an algorithm is found to be used but is unknown @@ -290,14 +294,14 @@ public final class SslContextFactory { } /** - * Creates a SSLContext instance paired with its TrustManager instances using the given information. + * Creates an SSLContext instance paired with its TrustManager instances using the given information. * * @param truststore the full path to the truststore * @param truststorePasswd the truststore password * @param truststoreType the type of truststore (e.g., PKCS12, JKS) * @param protocol the protocol to use for the SSL connection * - * @return a {@link Tuple} pairing a SSLContext instance paired with its TrustManager instances + * @return a {@link Tuple} pairing an SSLContext instance paired with its TrustManager instances * @throws KeyStoreException if any issues accessing the keystore * @throws IOException for any problems loading the keystores * @throws NoSuchAlgorithmException if an algorithm is found to be used but is unknown @@ -324,4 +328,39 @@ public final class SslContextFactory { return new Tuple<>(ctx, trustManagers); } + + /** + * Creates an SSLContext instance paired with its TrustManager instances using the given information. + * + * @param keystore the full path to the keystore + * @param keystorePasswd the keystore password + * @param keyPasswd the password for the key within the keystore + * @param keystoreType the type of keystore (e.g., PKCS12, JKS) + * @param truststore the full path to the truststore + * @param truststorePasswd the truststore password + * @param truststoreType the type of truststore (e.g., PKCS12, JKS) + * @param clientAuth the type of client authentication + * @param protocol the protocol to use for the SSL connection + * + * @return a {@link Tuple} pairing an SSLSocketFactory instance with its TrustManagers + * + */ + public static Tuple createTrustSslContextWithTrustManagers( + final String keystore, final char[] keystorePasswd, final char[] keyPasswd, final String keystoreType, + final String truststore, final char[] truststorePasswd, final String truststoreType, + final ClientAuth clientAuth, final String protocol) throws CertificateException, UnrecoverableKeyException, + NoSuchAlgorithmException, KeyStoreException, KeyManagementException, IOException { + + final Tuple sslContextWithTrustManagers; + if (keystore == null) { + sslContextWithTrustManagers = createTrustSslContextWithTrustManagers(truststore, truststorePasswd, truststoreType, protocol); + } else if (truststore == null) { + sslContextWithTrustManagers = createSslContextWithTrustManagers(keystore, keystorePasswd, keyPasswd, keystoreType, protocol); + } else { + sslContextWithTrustManagers = createSslContextWithTrustManagers(keystore, keystorePasswd, keyPasswd, keystoreType, truststore, + truststorePasswd, truststoreType, clientAuth, protocol); + } + return new Tuple<>(sslContextWithTrustManagers.getKey(), sslContextWithTrustManagers.getValue()); + + } } diff --git a/nifi-nar-bundles/nifi-elasticsearch-bundle/nifi-elasticsearch-processors/pom.xml b/nifi-nar-bundles/nifi-elasticsearch-bundle/nifi-elasticsearch-processors/pom.xml index e5a1def8fa..9142a899ed 100644 --- a/nifi-nar-bundles/nifi-elasticsearch-bundle/nifi-elasticsearch-processors/pom.xml +++ b/nifi-nar-bundles/nifi-elasticsearch-bundle/nifi-elasticsearch-processors/pom.xml @@ -95,7 +95,7 @@ language governing permissions and limitations under the License. --> com.squareup.okhttp3 okhttp - 3.3.1 + 3.14.4 org.apache.nifi diff --git a/nifi-nar-bundles/nifi-elasticsearch-bundle/nifi-elasticsearch-processors/src/main/java/org/apache/nifi/processors/elasticsearch/AbstractElasticsearchHttpProcessor.java b/nifi-nar-bundles/nifi-elasticsearch-bundle/nifi-elasticsearch-processors/src/main/java/org/apache/nifi/processors/elasticsearch/AbstractElasticsearchHttpProcessor.java index 5787e5999a..49a18658b8 100644 --- a/nifi-nar-bundles/nifi-elasticsearch-bundle/nifi-elasticsearch-processors/src/main/java/org/apache/nifi/processors/elasticsearch/AbstractElasticsearchHttpProcessor.java +++ b/nifi-nar-bundles/nifi-elasticsearch-bundle/nifi-elasticsearch-processors/src/main/java/org/apache/nifi/processors/elasticsearch/AbstractElasticsearchHttpProcessor.java @@ -36,20 +36,31 @@ import org.apache.nifi.processor.exception.ProcessException; import org.apache.nifi.processor.util.StandardValidators; import org.apache.nifi.proxy.ProxyConfiguration; import org.apache.nifi.proxy.ProxySpec; +import org.apache.nifi.security.util.SslContextFactory; import org.apache.nifi.ssl.SSLContextService; import org.apache.nifi.util.StringUtils; +import org.apache.nifi.util.Tuple; import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; import java.io.IOException; import java.io.InputStream; import java.net.Proxy; import java.net.URL; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; /** * A base class for Elasticsearch processors that use the HTTP API @@ -212,7 +223,25 @@ public abstract class AbstractElasticsearchHttpProcessor extends AbstractElastic // check if the ssl context is set and add the factory if so if (sslContext != null) { - okHttpClient.sslSocketFactory(sslContext.getSocketFactory()); + try { + Tuple sslContextTuple = SslContextFactory.createTrustSslContextWithTrustManagers( + sslService.getKeyStoreFile(), + sslService.getKeyStorePassword() != null ? sslService.getKeyStorePassword().toCharArray() : null, + sslService.getKeyPassword() != null ? sslService.getKeyPassword().toCharArray() : null, + sslService.getKeyStoreType(), + sslService.getTrustStoreFile(), + sslService.getTrustStorePassword() != null ? sslService.getTrustStorePassword().toCharArray() : null, + sslService.getTrustStoreType(), + SslContextFactory.ClientAuth.WANT, + sslService.getSslAlgorithm() + ); + List x509TrustManagers = Arrays.stream(sslContextTuple.getValue()) + .filter(trustManager -> trustManager instanceof X509TrustManager) + .map(trustManager -> (X509TrustManager) trustManager).collect(Collectors.toList()); + okHttpClient.sslSocketFactory(sslContextTuple.getKey().getSocketFactory(), x509TrustManagers.get(0)); + } catch (CertificateException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException | IOException e) { + throw new ProcessException(e); + } } okHttpClientAtomicReference.set(okHttpClient.build()); diff --git a/nifi-nar-bundles/nifi-elasticsearch-bundle/nifi-elasticsearch-processors/src/test/java/org/apache/nifi/processors/elasticsearch/TestQueryElasticsearchHttpNoHits.java b/nifi-nar-bundles/nifi-elasticsearch-bundle/nifi-elasticsearch-processors/src/test/java/org/apache/nifi/processors/elasticsearch/TestQueryElasticsearchHttpNoHits.java index b648ec8dfa..6b6d906c1f 100644 --- a/nifi-nar-bundles/nifi-elasticsearch-bundle/nifi-elasticsearch-processors/src/test/java/org/apache/nifi/processors/elasticsearch/TestQueryElasticsearchHttpNoHits.java +++ b/nifi-nar-bundles/nifi-elasticsearch-bundle/nifi-elasticsearch-processors/src/test/java/org/apache/nifi/processors/elasticsearch/TestQueryElasticsearchHttpNoHits.java @@ -257,7 +257,7 @@ public class TestQueryElasticsearchHttpNoHits { if (expectHitCountOnQueryInfo) { out.assertAttributeEquals("es.query.hitcount", String.valueOf(expectedHits)); } - Assert.assertTrue(out.getAttribute("es.query.url").startsWith("http://127.0.0.1:9200/doc/status/_search?q=source:Twitter%20AND%20identifier:%22%22&size=2")); + Assert.assertTrue(out.getAttribute("es.query.url").startsWith("http://127.0.0.1:9200/doc/status/_search?q=source%3ATwitter%20AND%20identifier%3A%22%22&size=2")); } } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/replication/okhttp/OkHttpReplicationClient.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/replication/okhttp/OkHttpReplicationClient.java index 81229de57b..2352026d1d 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/replication/okhttp/OkHttpReplicationClient.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/replication/okhttp/OkHttpReplicationClient.java @@ -23,12 +23,16 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URI; -import java.security.KeyStore; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -39,12 +43,9 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.zip.GZIPInputStream; -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; import javax.ws.rs.HttpMethod; import javax.ws.rs.core.MultivaluedHashMap; @@ -70,6 +71,9 @@ import org.apache.nifi.util.Tuple; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.StreamUtils; +// Using static imports because of the name conflict: +import static org.apache.nifi.security.util.SslContextFactory.ClientAuth.WANT; +import static org.apache.nifi.security.util.SslContextFactory.createTrustSslContextWithTrustManagers; public class OkHttpReplicationClient implements HttpReplicationClient { private static final Logger logger = LoggerFactory.getLogger(OkHttpReplicationClient.class); @@ -103,7 +107,8 @@ public class OkHttpReplicationClient implements HttpReplicationClient { /** * Checks the content length header on DELETE requests to ensure it is set to '0', avoiding request timeouts on replicated requests. - * @param method the HTTP method of the request + * + * @param method the HTTP method of the request * @param headers the header keys and values */ private void checkContentLengthHeader(String method, Map headers) { @@ -327,57 +332,22 @@ public class OkHttpReplicationClient implements HttpReplicationClient { } try { - final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509"); - - // initialize the KeyManager array to null and we will overwrite later if a keystore is loaded - KeyManager[] keyManagers = null; - - // we will only initialize the keystore if properties have been supplied by the SSLContextService - final String keystoreLocation = properties.getProperty(NiFiProperties.SECURITY_KEYSTORE); - final String keystorePass = properties.getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD); - final String keystoreType = properties.getProperty(NiFiProperties.SECURITY_KEYSTORE_TYPE); - - // prepare the keystore - final KeyStore keyStore = KeyStore.getInstance(keystoreType); - - try (FileInputStream keyStoreStream = new FileInputStream(keystoreLocation)) { - keyStore.load(keyStoreStream, keystorePass.toCharArray()); - } - - keyManagerFactory.init(keyStore, keystorePass.toCharArray()); - keyManagers = keyManagerFactory.getKeyManagers(); - - // we will only initialize the truststure if properties have been supplied by the SSLContextService - // load truststore - final String truststoreLocation = properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE); - final String truststorePass = properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD); - final String truststoreType = properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_TYPE); - - KeyStore truststore = KeyStore.getInstance(truststoreType); - truststore.load(new FileInputStream(truststoreLocation), truststorePass.toCharArray()); - trustManagerFactory.init(truststore); - - // TrustManagerFactory.getTrustManagers returns a trust manager for each type of trust material. Since we are getting a trust manager factory that uses "X509" - // as it's trust management algorithm, we are able to grab the first (and thus the most preferred) and use it as our x509 Trust Manager - // - // https://docs.oracle.com/javase/8/docs/api/javax/net/ssl/TrustManagerFactory.html#getTrustManagers-- - final X509TrustManager x509TrustManager; - TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); - if (trustManagers[0] != null) { - x509TrustManager = (X509TrustManager) trustManagers[0]; - } else { - throw new IllegalStateException("List of trust managers is null"); - } - - // if keystore properties were not supplied, the keyManagers array will be null - sslContext.init(keyManagers, trustManagerFactory.getTrustManagers(), null); - - final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); - return new Tuple<>(sslSocketFactory, x509TrustManager); - } catch (final Exception e) { - throw new RuntimeException("Failed to create SSL Socket Factory for replicating requests across the cluster"); + Tuple sslContextTuple = createTrustSslContextWithTrustManagers( + properties.getProperty(NiFiProperties.SECURITY_KEYSTORE), + properties.getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD) != null ? properties.getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD).toCharArray() : null, + properties.getProperty(NiFiProperties.SECURITY_KEY_PASSWD) != null ? properties.getProperty(NiFiProperties.SECURITY_KEY_PASSWD).toCharArray() : null, + properties.getProperty(NiFiProperties.SECURITY_KEYSTORE_TYPE), + properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE), + properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD) != null ? properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD).toCharArray() : null, + properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_TYPE), + WANT, + sslContext.getProtocol()); + List x509TrustManagers = Arrays.stream(sslContextTuple.getValue()) + .filter(trustManager -> trustManager instanceof X509TrustManager) + .map(trustManager -> (X509TrustManager) trustManager).collect(Collectors.toList()); + return new Tuple<>(sslContextTuple.getKey().getSocketFactory(), x509TrustManagers.get(0)); + } catch (CertificateException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException | IOException e) { + return null; } } - } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-security/src/main/java/org/apache/nifi/framework/security/util/SslServerSocketFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-security/src/main/java/org/apache/nifi/framework/security/util/SslServerSocketFactory.java deleted file mode 100644 index 458157cb8e..0000000000 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-security/src/main/java/org/apache/nifi/framework/security/util/SslServerSocketFactory.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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.framework.security.util; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.ServerSocket; -import java.security.NoSuchAlgorithmException; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLServerSocketFactory; -import org.apache.nifi.util.NiFiProperties; - -/** - * Implements a server socket factory for creating secure server sockets based - * on the application's configuration properties. If the properties are - * configured for SSL (one-way or two-way), then a SSLServerSocketFactory is - * created and used based on those properties. Otherwise, Java's default - * SSLServerSocketFactory is used. Specifically, - * SSLContext.getDefault().getServerSocketFactory(). - */ -public class SslServerSocketFactory extends SSLServerSocketFactory { - - private SSLServerSocketFactory sslServerSocketFactory; - - public SslServerSocketFactory(final NiFiProperties nifiProperties) { - final SSLContext sslCtx = SslContextFactory.createSslContext(nifiProperties); - if (sslCtx == null) { - try { - sslServerSocketFactory = SSLContext.getDefault().getServerSocketFactory(); - } catch (final NoSuchAlgorithmException nsae) { - throw new SslServerSocketFactoryCreationException(nsae); - } - } else { - sslServerSocketFactory = sslCtx.getServerSocketFactory(); - } - } - - @Override - public ServerSocket createServerSocket(int i, int i1, InetAddress ia) throws IOException { - return sslServerSocketFactory.createServerSocket(i, i1, ia); - } - - @Override - public ServerSocket createServerSocket(int i, int i1) throws IOException { - return sslServerSocketFactory.createServerSocket(i, i1); - } - - @Override - public ServerSocket createServerSocket(int i) throws IOException { - return sslServerSocketFactory.createServerSocket(i); - } - - @Override - public ServerSocket createServerSocket() throws IOException { - return sslServerSocketFactory.createServerSocket(); - } - - @Override - public String[] getSupportedCipherSuites() { - return sslServerSocketFactory.getSupportedCipherSuites(); - } - - @Override - public String[] getDefaultCipherSuites() { - return sslServerSocketFactory.getDefaultCipherSuites(); - } - -} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-security/src/main/java/org/apache/nifi/framework/security/util/SslSocketFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-security/src/main/java/org/apache/nifi/framework/security/util/SslSocketFactory.java deleted file mode 100644 index fa6de56579..0000000000 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-security/src/main/java/org/apache/nifi/framework/security/util/SslSocketFactory.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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.framework.security.util; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.Socket; -import java.net.UnknownHostException; -import java.security.NoSuchAlgorithmException; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocketFactory; -import org.apache.nifi.util.NiFiProperties; - -/** - * Implements a socket factory for creating secure sockets based on the - * application's configuration properties. If the properties are configured for - * SSL (one-way or two-way), then a SSLSocketFactory is created and used based - * on those properties. Otherwise, Java's default SSLSocketFactory is used. - * Specifically, SSLContext.getDefault().getSocketFactory(). - */ -public class SslSocketFactory extends SSLSocketFactory { - - private final SSLSocketFactory sslSocketFactory; - - public SslSocketFactory(final NiFiProperties nifiProperties) { - final SSLContext sslCtx = SslContextFactory.createSslContext(nifiProperties); - if (sslCtx == null) { - try { - sslSocketFactory = SSLContext.getDefault().getSocketFactory(); - } catch (final NoSuchAlgorithmException nsae) { - throw new SslSocketFactoryCreationException(nsae); - } - } else { - sslSocketFactory = sslCtx.getSocketFactory(); - } - } - - @Override - public String[] getSupportedCipherSuites() { - return sslSocketFactory.getSupportedCipherSuites(); - } - - @Override - public String[] getDefaultCipherSuites() { - return sslSocketFactory.getDefaultCipherSuites(); - } - - @Override - public Socket createSocket(Socket socket, String string, int i, boolean bln) throws IOException { - return sslSocketFactory.createSocket(socket, string, i, bln); - } - - @Override - public Socket createSocket(InetAddress ia, int i, InetAddress ia1, int i1) throws IOException { - return sslSocketFactory.createSocket(ia, i, ia1, i1); - } - - @Override - public Socket createSocket(InetAddress ia, int i) throws IOException { - return sslSocketFactory.createSocket(ia, i); - } - - @Override - public Socket createSocket(String string, int i, InetAddress ia, int i1) throws IOException, UnknownHostException { - return sslSocketFactory.createSocket(string, i, ia, i1); - } - - @Override - public Socket createSocket(String string, int i) throws IOException, UnknownHostException { - return sslSocketFactory.createSocket(string, i); - } - - @Override - public Socket createSocket() throws IOException { - return sslSocketFactory.createSocket(); - } - -} diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/InvokeHTTP.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/InvokeHTTP.java index c344410958..78cadab5da 100644 --- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/InvokeHTTP.java +++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/InvokeHTTP.java @@ -55,6 +55,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; @@ -104,9 +105,11 @@ import org.apache.nifi.processors.standard.util.ProxyAuthenticator; import org.apache.nifi.processors.standard.util.SoftLimitBoundedByteArrayOutputStream; import org.apache.nifi.proxy.ProxyConfiguration; import org.apache.nifi.proxy.ProxySpec; +import org.apache.nifi.security.util.SslContextFactory; import org.apache.nifi.ssl.SSLContextService; import org.apache.nifi.ssl.SSLContextService.ClientAuth; import org.apache.nifi.stream.io.StreamUtils; +import org.apache.nifi.util.Tuple; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; @@ -634,8 +637,21 @@ public final class InvokeHTTP extends AbstractProcessor { // check if the ssl context is set and add the factory if so if (sslContext != null) { - setSslSocketFactory(okHttpClientBuilder, sslService, sslContext, isHttpsProxy); - } + Tuple sslContextTuple =SslContextFactory.createTrustSslContextWithTrustManagers( + sslService.getKeyStoreFile(), + sslService.getKeyStorePassword() != null ? sslService.getKeyStorePassword().toCharArray() : null, + sslService.getKeyPassword() != null ? sslService.getKeyPassword().toCharArray() : null, + sslService.getKeyStoreType(), + sslService.getTrustStoreFile(), + sslService.getTrustStorePassword() != null ? sslService.getTrustStorePassword().toCharArray() : null, + sslService.getTrustStoreType(), + SslContextFactory.ClientAuth.NONE, + sslService.getSslAlgorithm()); + List x509TrustManagers = Arrays.stream(sslContextTuple.getValue()) + .filter(trustManager -> trustManager instanceof X509TrustManager) + .map(trustManager -> (X509TrustManager) trustManager).collect(Collectors.toList()); + okHttpClientBuilder.sslSocketFactory(sslContextTuple.getKey().getSocketFactory(), x509TrustManagers.get(0)); + } setAuthenticator(okHttpClientBuilder, context); diff --git a/nifi-nar-bundles/nifi-standard-services/nifi-lookup-services-bundle/nifi-lookup-services/src/main/java/org/apache/nifi/lookup/RestLookupService.java b/nifi-nar-bundles/nifi-standard-services/nifi-lookup-services-bundle/nifi-lookup-services/src/main/java/org/apache/nifi/lookup/RestLookupService.java index 6ecc95d894..882690de5d 100644 --- a/nifi-nar-bundles/nifi-standard-services/nifi-lookup-services-bundle/nifi-lookup-services/src/main/java/org/apache/nifi/lookup/RestLookupService.java +++ b/nifi-nar-bundles/nifi-standard-services/nifi-lookup-services-bundle/nifi-lookup-services/src/main/java/org/apache/nifi/lookup/RestLookupService.java @@ -40,6 +40,7 @@ import org.apache.nifi.components.Validator; import org.apache.nifi.controller.AbstractControllerService; import org.apache.nifi.controller.ConfigurationContext; import org.apache.nifi.expression.ExpressionLanguageScope; +import org.apache.nifi.processor.exception.ProcessException; import org.apache.nifi.processor.util.StandardValidators; import org.apache.nifi.proxy.ProxyConfiguration; import org.apache.nifi.proxy.ProxyConfigurationService; @@ -48,6 +49,7 @@ import org.apache.nifi.record.path.FieldValue; import org.apache.nifi.record.path.RecordPath; import org.apache.nifi.record.path.validation.RecordPathValidator; import org.apache.nifi.schema.access.SchemaNotFoundException; +import org.apache.nifi.security.util.SslContextFactory; import org.apache.nifi.serialization.MalformedRecordException; import org.apache.nifi.serialization.RecordReader; import org.apache.nifi.serialization.RecordReaderFactory; @@ -57,12 +59,20 @@ import org.apache.nifi.serialization.record.Record; import org.apache.nifi.serialization.record.RecordSchema; import org.apache.nifi.ssl.SSLContextService; import org.apache.nifi.util.StringUtils; +import org.apache.nifi.util.Tuple; import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.net.Proxy; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -231,9 +241,33 @@ public class RestLookupService extends AbstractControllerService implements Reco } final SSLContextService sslService = context.getProperty(SSL_CONTEXT_SERVICE).asControllerService(SSLContextService.class); - final SSLContext sslContext = sslService == null ? null : sslService.createSSLContext(SSLContextService.ClientAuth.WANT); if (sslService != null) { - builder.sslSocketFactory(sslContext.getSocketFactory()); + Tuple sslContextTuple = null; + try { + sslContextTuple = SslContextFactory.createTrustSslContextWithTrustManagers( + sslService.getKeyStoreFile(), + sslService.getKeyStorePassword() != null ? sslService.getKeyStorePassword().toCharArray() : null, + sslService.getKeyPassword() != null ? sslService.getKeyPassword().toCharArray() : null, + sslService.getKeyStoreType(), + sslService.getTrustStoreFile(), + sslService.getTrustStorePassword() != null ? sslService.getTrustStorePassword().toCharArray() : null, + sslService.getTrustStoreType(), + SslContextFactory.ClientAuth.WANT, + sslService.getSslAlgorithm() + ); + } catch (CertificateException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException | IOException e) { + throw new ProcessException(e); + } + List x509TrustManagers = Arrays.stream(sslContextTuple.getValue()) + .filter(trustManager -> trustManager instanceof X509TrustManager) + .map(trustManager -> (X509TrustManager) trustManager).collect(Collectors.toList()); + builder.sslSocketFactory(sslContextTuple.getKey().getSocketFactory(), x509TrustManagers.get(0)); + + if (sslContextTuple.getValue().length > 0) { + builder.sslSocketFactory(sslContextTuple.getKey().getSocketFactory(), (X509TrustManager) sslContextTuple.getValue()[0]); + } else { + throw new ProcessException("Failed to create SSL socket factory with trust manager."); + } } client = builder.build();