From 01539ed3230894b062a7c2e42ffd9b50e3d51bf3 Mon Sep 17 00:00:00 2001 From: Aldrin Piri Date: Sat, 14 Nov 2015 18:43:49 -0500 Subject: [PATCH] NIFI-1163: Providing handling of SSLContext creation in GetHTTP in case of only performing a one-way SSL request and accompanying test to verify the configuration/usage. Reviewed by Tony Kurc (tkurc@apache.org) --- .../nifi/processors/standard/GetHTTP.java | 28 +++-- .../nifi/processors/standard/TestGetHTTP.java | 104 +++++++++++++----- 2 files changed, 92 insertions(+), 40 deletions(-) diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/GetHTTP.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/GetHTTP.java index e846b82c87..224508059a 100644 --- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/GetHTTP.java +++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/GetHTTP.java @@ -49,6 +49,7 @@ import java.util.regex.Pattern; import javax.net.ssl.SSLContext; +import org.apache.commons.lang3.StringUtils; import org.apache.http.Header; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; @@ -64,11 +65,11 @@ import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; -import org.apache.http.conn.ssl.SSLContexts; import org.apache.http.conn.ssl.TrustSelfSignedStrategy; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.BasicHttpClientConnectionManager; +import org.apache.http.ssl.SSLContextBuilder; import org.apache.nifi.annotation.behavior.InputRequirement; import org.apache.nifi.annotation.behavior.InputRequirement.Requirement; import org.apache.nifi.annotation.behavior.WritesAttribute; @@ -320,19 +321,26 @@ public class GetHTTP extends AbstractSessionFactoryProcessor { private SSLContext createSSLContext(final SSLContextService service) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, KeyManagementException, UnrecoverableKeyException { - final KeyStore truststore = KeyStore.getInstance(service.getTrustStoreType()); - try (final InputStream in = new FileInputStream(new File(service.getTrustStoreFile()))) { - truststore.load(in, service.getTrustStorePassword().toCharArray()); + + final SSLContextBuilder sslContextBuilder = new SSLContextBuilder(); + + if (StringUtils.isNotBlank(service.getTrustStoreFile())) { + final KeyStore truststore = KeyStore.getInstance(service.getTrustStoreType()); + try (final InputStream in = new FileInputStream(new File(service.getTrustStoreFile()))) { + truststore.load(in, service.getTrustStorePassword().toCharArray()); + } + sslContextBuilder.loadTrustMaterial(truststore, new TrustSelfSignedStrategy()); } - final KeyStore keystore = KeyStore.getInstance(service.getKeyStoreType()); - try (final InputStream in = new FileInputStream(new File(service.getKeyStoreFile()))) { - keystore.load(in, service.getKeyStorePassword().toCharArray()); + if (StringUtils.isNotBlank(service.getKeyStoreFile())){ + final KeyStore keystore = KeyStore.getInstance(service.getKeyStoreType()); + try (final InputStream in = new FileInputStream(new File(service.getKeyStoreFile()))) { + keystore.load(in, service.getKeyStorePassword().toCharArray()); + } + sslContextBuilder.loadKeyMaterial(keystore, service.getKeyStorePassword().toCharArray()); } - final SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(truststore, new TrustSelfSignedStrategy()).loadKeyMaterial(keystore, service.getKeyStorePassword().toCharArray()).build(); - - return sslContext; + return sslContextBuilder.build(); } @Override diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestGetHTTP.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestGetHTTP.java index bb3d28685b..29ce4299f5 100644 --- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestGetHTTP.java +++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestGetHTTP.java @@ -317,48 +317,27 @@ public class TestGetHTTP { } } - private Map getSslProperties() { - Map props = new HashMap(); - props.put(StandardSSLContextService.KEYSTORE.getName(), "src/test/resources/localhost-ks.jks"); - props.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), "localtest"); - props.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), "JKS"); - props.put(StandardSSLContextService.TRUSTSTORE.getName(), "src/test/resources/localhost-ts.jks"); - props.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), "localtest"); - props.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), "JKS"); - return props; - } - - private void useSSLContextService() { - final SSLContextService service = new StandardSSLContextService(); - try { - controller.addControllerService("ssl-service", service, getSslProperties()); - controller.enableControllerService(service); - } catch (InitializationException ex) { - ex.printStackTrace(); - Assert.fail("Could not create SSL Context Service"); - } - - controller.setProperty(GetHTTP.SSL_CONTEXT_SERVICE, "ssl-service"); - } - @Test - public final void testSecure() throws Exception { + public final void testSecure_oneWaySsl() throws Exception { // set up web service - ServletHandler handler = new ServletHandler(); + final ServletHandler handler = new ServletHandler(); handler.addServletWithMapping(HelloWorldServlet.class, "/*"); - // create the service - TestServer server = new TestServer(getSslProperties()); + // create the service, disabling the need for client auth + final Map serverSslProperties = getKeystoreProperties(); + serverSslProperties.put(TestServer.NEED_CLIENT_AUTH, Boolean.toString(false)); + final TestServer server = new TestServer(serverSslProperties); server.addHandler(handler); try { server.startServer(); - String destination = server.getSecureUrl(); + final String destination = server.getSecureUrl(); // set up NiFi mock controller controller = TestRunners.newTestRunner(GetHTTP.class); - useSSLContextService(); + // Use context service with only a truststore + useSSLContextService(getTruststoreProperties()); controller.setProperty(GetHTTP.CONNECTION_TIMEOUT, "5 secs"); controller.setProperty(GetHTTP.URL, destination); @@ -374,4 +353,69 @@ public class TestGetHTTP { } } + @Test + public final void testSecure_twoWaySsl() throws Exception { + // set up web service + final ServletHandler handler = new ServletHandler(); + handler.addServletWithMapping(HelloWorldServlet.class, "/*"); + + // create the service, providing both truststore and keystore properties, requiring client auth (default) + final Map twoWaySslProperties = getKeystoreProperties(); + twoWaySslProperties.putAll(getTruststoreProperties()); + final TestServer server = new TestServer(twoWaySslProperties); + server.addHandler(handler); + + try { + server.startServer(); + + final String destination = server.getSecureUrl(); + + // set up NiFi mock controller + controller = TestRunners.newTestRunner(GetHTTP.class); + // Use context service with a keystore and a truststore + useSSLContextService(twoWaySslProperties); + + controller.setProperty(GetHTTP.CONNECTION_TIMEOUT, "5 secs"); + controller.setProperty(GetHTTP.URL, destination); + controller.setProperty(GetHTTP.FILENAME, "testFile"); + controller.setProperty(GetHTTP.ACCEPT_CONTENT_TYPE, "application/json"); + + controller.run(); + controller.assertAllFlowFilesTransferred(GetHTTP.REL_SUCCESS, 1); + final MockFlowFile mff = controller.getFlowFilesForRelationship(GetHTTP.REL_SUCCESS).get(0); + mff.assertContentEquals("Hello, World!"); + } finally { + server.shutdownServer(); + } + } + + private static Map getTruststoreProperties() { + final Map props = new HashMap<>(); + props.put(StandardSSLContextService.TRUSTSTORE.getName(), "src/test/resources/localhost-ts.jks"); + props.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), "localtest"); + props.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), "JKS"); + return props; + } + + private static Map getKeystoreProperties() { + final Map properties = new HashMap<>(); + properties.put(StandardSSLContextService.KEYSTORE.getName(), "src/test/resources/localhost-ks.jks"); + properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), "localtest"); + properties.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), "JKS"); + return properties; + } + + private void useSSLContextService(final Map sslProperties) { + final SSLContextService service = new StandardSSLContextService(); + try { + controller.addControllerService("ssl-service", service, sslProperties); + controller.enableControllerService(service); + } catch (InitializationException ex) { + ex.printStackTrace(); + Assert.fail("Could not create SSL Context Service"); + } + + controller.setProperty(GetHTTP.SSL_CONTEXT_SERVICE, "ssl-service"); + } + }