NIFI-6927 Consolidate SSL context and trust managers for OkHttp on JDK9.

Fixes name conflicts.

This closes #4047.

Signed-off-by: Andy LoPresto <alopresto@apache.org>
This commit is contained in:
Troy Melhase 2020-02-10 11:56:09 -09:00 committed by Andy LoPresto
parent 4098404596
commit 0de89452f1
No known key found for this signature in database
GPG Key ID: 6EC293152D90B61D
10 changed files with 184 additions and 307 deletions

View File

@ -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<SSLSocketFactory, TrustManager[]> sslSocketFactoryWithTrustManagers = getSslSocketFactory(context);
Tuple<SSLContext, TrustManager[]> 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<X509TrustManager> x509TrustManagers = Arrays.stream(sslSocketFactoryWithTrustManagers.getValue())
List<X509TrustManager> 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<SSLSocketFactory, TrustManager[]> 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<SSLContext, TrustManager[]> 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);
}
}
}

View File

@ -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<SSLContext, TrustManager[]> 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<SSLContext, TrustManager[]> 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());
}
}

View File

@ -95,7 +95,7 @@ language governing permissions and limitations under the License. -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.3.1</version>
<version>3.14.4</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>

View File

@ -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<SSLContext, TrustManager[]> 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<X509TrustManager> 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());

View File

@ -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"));
}
}
}

View File

@ -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<String, String> 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<SSLContext, TrustManager[]> 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<X509TrustManager> 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;
}
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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<SSLContext, TrustManager[]> 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<X509TrustManager> 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);

View File

@ -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<SSLContext, TrustManager[]> 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<X509TrustManager> 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();