From 64502d0e10e964bebddd0ad35c4c074f002d536d Mon Sep 17 00:00:00 2001 From: Aldrin Piri Date: Tue, 9 Jun 2015 00:54:26 -0400 Subject: [PATCH] NIFI-419 Providing a user configurable selection of the SSL Protocol algorithm used for connections withing an SSL Context controller service. This provides defaults of TLS/SSL values as well as performs a look up of the available items within the JVM. --- .../nifi/processor/util/SSLProperties.java | 10 +- .../nifi/security/util/SslContextFactory.java | 15 +-- .../nifi/ssl/StandardSSLContextService.java | 91 +++++++++++++++---- .../apache/nifi/ssl/SSLContextService.java | 1 + 4 files changed, 91 insertions(+), 26 deletions(-) diff --git a/nifi/nifi-commons/nifi-processor-utilities/src/main/java/org/apache/nifi/processor/util/SSLProperties.java b/nifi/nifi-commons/nifi-processor-utilities/src/main/java/org/apache/nifi/processor/util/SSLProperties.java index 87d63de017..dca15b0409 100644 --- a/nifi/nifi-commons/nifi-processor-utilities/src/main/java/org/apache/nifi/processor/util/SSLProperties.java +++ b/nifi/nifi-commons/nifi-processor-utilities/src/main/java/org/apache/nifi/processor/util/SSLProperties.java @@ -163,6 +163,8 @@ public class SSLProperties { KEYSTORE, TRUSTSTORE } + private static final String DEFAULT_SSL_PROTOCOL_ALGORITHM = "TLS"; + public static List getKeystoreDescriptors(final boolean required) { final List descriptors = new ArrayList<>(); for (final PropertyDescriptor descriptor : KEYSTORE_DESCRIPTORS) { @@ -196,14 +198,15 @@ public class SSLProperties { return SslContextFactory.createTrustSslContext( context.getProperty(TRUSTSTORE).getValue(), context.getProperty(TRUSTSTORE_PASSWORD).getValue().toCharArray(), - context.getProperty(TRUSTSTORE_TYPE).getValue()); + context.getProperty(TRUSTSTORE_TYPE).getValue(), + DEFAULT_SSL_PROTOCOL_ALGORITHM); } else { final String truststoreFile = context.getProperty(TRUSTSTORE).getValue(); if (truststoreFile == null) { return SslContextFactory.createSslContext( context.getProperty(KEYSTORE).getValue(), context.getProperty(KEYSTORE_PASSWORD).getValue().toCharArray(), - context.getProperty(KEYSTORE_TYPE).getValue()); + context.getProperty(KEYSTORE_TYPE).getValue(), DEFAULT_SSL_PROTOCOL_ALGORITHM); } else { return SslContextFactory.createSslContext( context.getProperty(KEYSTORE).getValue(), @@ -212,7 +215,8 @@ public class SSLProperties { context.getProperty(TRUSTSTORE).getValue(), context.getProperty(TRUSTSTORE_PASSWORD).getValue().toCharArray(), context.getProperty(TRUSTSTORE_TYPE).getValue(), - clientAuth); + clientAuth, + DEFAULT_SSL_PROTOCOL_ALGORITHM); } } } diff --git a/nifi/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/SslContextFactory.java b/nifi/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/SslContextFactory.java index 78cf6e676b..656573db1a 100644 --- a/nifi/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/SslContextFactory.java +++ b/nifi/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/SslContextFactory.java @@ -57,6 +57,7 @@ public final class SslContextFactory { * @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 SSLContext instance * @throws java.security.KeyStoreException if any issues accessing the keystore @@ -69,7 +70,7 @@ public final class SslContextFactory { public static SSLContext createSslContext( final String keystore, final char[] keystorePasswd, final String keystoreType, final String truststore, final char[] truststorePasswd, final String truststoreType, - final ClientAuth clientAuth) + final ClientAuth clientAuth, final String protocol) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException, KeyManagementException { @@ -90,7 +91,7 @@ public final class SslContextFactory { trustManagerFactory.init(trustStore); // initialize the ssl context - final SSLContext sslContext = SSLContext.getInstance("TLS"); + final SSLContext sslContext = SSLContext.getInstance(protocol); sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom()); if (ClientAuth.REQUIRED == clientAuth) { sslContext.getDefaultSSLParameters().setNeedClientAuth(true); @@ -110,6 +111,7 @@ public final class SslContextFactory { * @param keystore the full path to the keystore * @param keystorePasswd the keystore password * @param keystoreType the type of keystore (e.g., PKCS12, JKS) + * @param protocol the protocol to use for the SSL connection * * @return a SSLContext instance * @throws java.security.KeyStoreException if any issues accessing the keystore @@ -120,7 +122,7 @@ public final class SslContextFactory { * @throws java.security.KeyManagementException if unable to manage the key */ public static SSLContext createSslContext( - final String keystore, final char[] keystorePasswd, final String keystoreType) + final String keystore, final char[] keystorePasswd, final String keystoreType, final String protocol) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException, KeyManagementException { @@ -133,7 +135,7 @@ public final class SslContextFactory { keyManagerFactory.init(keyStore, keystorePasswd); // initialize the ssl context - final SSLContext ctx = SSLContext.getInstance("TLS"); + final SSLContext ctx = SSLContext.getInstance(protocol); ctx.init(keyManagerFactory.getKeyManagers(), new TrustManager[0], new SecureRandom()); return ctx; @@ -146,6 +148,7 @@ public final class SslContextFactory { * @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 * @throws java.security.KeyStoreException if any issues accessing the keystore @@ -156,7 +159,7 @@ public final class SslContextFactory { * @throws java.security.KeyManagementException if unable to manage the key */ public static SSLContext createTrustSslContext( - final String truststore, final char[] truststorePasswd, final String truststoreType) + final String truststore, final char[] truststorePasswd, final String truststoreType, final String protocol) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException, KeyManagementException { @@ -169,7 +172,7 @@ public final class SslContextFactory { trustManagerFactory.init(trustStore); // initialize the ssl context - final SSLContext ctx = SSLContext.getInstance("TLS"); + final SSLContext ctx = SSLContext.getInstance(protocol); ctx.init(new KeyManager[0], trustManagerFactory.getTrustManagers(), new SecureRandom()); return ctx; diff --git a/nifi/nifi-nar-bundles/nifi-standard-services/nifi-ssl-context-bundle/nifi-ssl-context-service/src/main/java/org/apache/nifi/ssl/StandardSSLContextService.java b/nifi/nifi-nar-bundles/nifi-standard-services/nifi-ssl-context-bundle/nifi-ssl-context-service/src/main/java/org/apache/nifi/ssl/StandardSSLContextService.java index cde71da5ce..8fa9100b68 100644 --- a/nifi/nifi-nar-bundles/nifi-standard-services/nifi-ssl-context-bundle/nifi-ssl-context-service/src/main/java/org/apache/nifi/ssl/StandardSSLContextService.java +++ b/nifi/nifi-nar-bundles/nifi-standard-services/nifi-ssl-context-bundle/nifi-ssl-context-service/src/main/java/org/apache/nifi/ssl/StandardSSLContextService.java @@ -16,19 +16,10 @@ */ package org.apache.nifi.ssl; -import java.io.File; -import java.net.MalformedURLException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import javax.net.ssl.SSLContext; - import org.apache.nifi.annotation.documentation.CapabilityDescription; import org.apache.nifi.annotation.documentation.Tags; import org.apache.nifi.annotation.lifecycle.OnEnabled; +import org.apache.nifi.components.AllowableValue; import org.apache.nifi.components.PropertyDescriptor; import org.apache.nifi.components.ValidationContext; import org.apache.nifi.components.ValidationResult; @@ -42,6 +33,19 @@ import org.apache.nifi.security.util.CertificateUtils; import org.apache.nifi.security.util.KeystoreType; import org.apache.nifi.security.util.SslContextFactory; +import javax.net.ssl.SSLContext; +import java.io.File; +import java.net.MalformedURLException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + @Tags({"ssl", "secure", "certificate", "keystore", "truststore", "jks", "p12", "pkcs12", "pkcs"}) @CapabilityDescription("Standard implementation of the SSLContextService. Provides the ability to configure " + "keystore and/or truststore properties once and reuse that configuration throughout the application") @@ -92,6 +96,15 @@ public class StandardSSLContextService extends AbstractControllerService impleme .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) .sensitive(true) .build(); + public static final PropertyDescriptor SSL_ALGORITHM = new PropertyDescriptor.Builder() + .name("SSL Protocol") + .defaultValue("TLS") + .required(false) + .allowableValues(buildAlgorithmAllowableValues()) + .description("The algorithm to use for this SSL context") + .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) + .sensitive(false) + .build(); private static final List properties; private ConfigurationContext configContext; @@ -104,6 +117,7 @@ public class StandardSSLContextService extends AbstractControllerService impleme props.add(TRUSTSTORE); props.add(TRUSTSTORE_PASSWORD); props.add(TRUSTSTORE_TYPE); + props.add(SSL_ALGORITHM); properties = Collections.unmodifiableList(props); } @@ -207,13 +221,15 @@ public class StandardSSLContextService extends AbstractControllerService impleme } private void verifySslConfig(final ValidationContext validationContext) throws ProcessException { + final String protocol = validationContext.getProperty(SSL_ALGORITHM).getValue(); try { final String keystoreFile = validationContext.getProperty(KEYSTORE).getValue(); if (keystoreFile == null) { SslContextFactory.createTrustSslContext( validationContext.getProperty(TRUSTSTORE).getValue(), validationContext.getProperty(TRUSTSTORE_PASSWORD).getValue().toCharArray(), - validationContext.getProperty(TRUSTSTORE_TYPE).getValue()); + validationContext.getProperty(TRUSTSTORE_TYPE).getValue(), + protocol); return; } final String truststoreFile = validationContext.getProperty(TRUSTSTORE).getValue(); @@ -221,7 +237,8 @@ public class StandardSSLContextService extends AbstractControllerService impleme SslContextFactory.createSslContext( validationContext.getProperty(KEYSTORE).getValue(), validationContext.getProperty(KEYSTORE_PASSWORD).getValue().toCharArray(), - validationContext.getProperty(KEYSTORE_TYPE).getValue()); + validationContext.getProperty(KEYSTORE_TYPE).getValue(), + protocol); return; } @@ -232,7 +249,8 @@ public class StandardSSLContextService extends AbstractControllerService impleme validationContext.getProperty(TRUSTSTORE).getValue(), validationContext.getProperty(TRUSTSTORE_PASSWORD).getValue().toCharArray(), validationContext.getProperty(TRUSTSTORE_TYPE).getValue(), - org.apache.nifi.security.util.SslContextFactory.ClientAuth.REQUIRED); + org.apache.nifi.security.util.SslContextFactory.ClientAuth.REQUIRED, + protocol); } catch (final Exception e) { throw new ProcessException(e); } @@ -240,20 +258,23 @@ public class StandardSSLContextService extends AbstractControllerService impleme @Override public SSLContext createSSLContext(final ClientAuth clientAuth) throws ProcessException { + final String protocol = configContext.getProperty(SSL_ALGORITHM).getValue(); try { final String keystoreFile = configContext.getProperty(KEYSTORE).getValue(); if (keystoreFile == null) { return SslContextFactory.createTrustSslContext( configContext.getProperty(TRUSTSTORE).getValue(), configContext.getProperty(TRUSTSTORE_PASSWORD).getValue().toCharArray(), - configContext.getProperty(TRUSTSTORE_TYPE).getValue()); + configContext.getProperty(TRUSTSTORE_TYPE).getValue(), + protocol); } final String truststoreFile = configContext.getProperty(TRUSTSTORE).getValue(); if (truststoreFile == null) { return SslContextFactory.createSslContext( configContext.getProperty(KEYSTORE).getValue(), configContext.getProperty(KEYSTORE_PASSWORD).getValue().toCharArray(), - configContext.getProperty(KEYSTORE_TYPE).getValue()); + configContext.getProperty(KEYSTORE_TYPE).getValue(), + protocol); } return SslContextFactory.createSslContext( @@ -263,7 +284,8 @@ public class StandardSSLContextService extends AbstractControllerService impleme configContext.getProperty(TRUSTSTORE).getValue(), configContext.getProperty(TRUSTSTORE_PASSWORD).getValue().toCharArray(), configContext.getProperty(TRUSTSTORE_TYPE).getValue(), - org.apache.nifi.security.util.SslContextFactory.ClientAuth.valueOf(clientAuth.name())); + org.apache.nifi.security.util.SslContextFactory.ClientAuth.valueOf(clientAuth.name()), + protocol); } catch (final Exception e) { throw new ProcessException(e); } @@ -309,8 +331,13 @@ public class StandardSSLContextService extends AbstractControllerService impleme return getKeyStoreFile() != null && getKeyStorePassword() != null && getKeyStoreType() != null; } + @Override + public String getSslAlgorithm() { + return configContext.getProperty(SSL_ALGORITHM).getValue(); + } + private static Collection validateStore(final Map properties, - final KeystoreValidationGroup keyStoreOrTrustStore) { + final KeystoreValidationGroup keyStoreOrTrustStore) { final Collection results = new ArrayList<>(); final String filename; @@ -382,6 +409,36 @@ public class StandardSSLContextService extends AbstractControllerService impleme KEYSTORE, TRUSTSTORE } + private static AllowableValue[] buildAlgorithmAllowableValues() { + final Set supportedProtocols = new HashSet<>(); + + /* + * Prepopulate protocols with generic instance types commonly used + * see: http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#SSLContext + */ + supportedProtocols.add("SSL"); + supportedProtocols.add("TLS"); + + // Determine those provided by the JVM on the system + try { + supportedProtocols.addAll(Arrays.asList(SSLContext.getDefault().createSSLEngine().getSupportedProtocols())); + } catch (NoSuchAlgorithmException e) { + // ignored as default is used + } + + final int numProtocols = supportedProtocols.size(); + + // Sort for consistent presentation in configuraiton views + final List supportedProtocolList = new ArrayList<>(supportedProtocols); + Collections.sort(supportedProtocolList); + + final List protocolAllowableValues = new ArrayList<>(); + for (final String protocol : supportedProtocolList) { + protocolAllowableValues.add(new AllowableValue(protocol)); + } + return protocolAllowableValues.toArray(new AllowableValue[numProtocols]); + } + @Override public String toString() { return "SSLContextService[id=" + getIdentifier() + "]"; diff --git a/nifi/nifi-nar-bundles/nifi-standard-services/nifi-ssl-context-service-api/src/main/java/org/apache/nifi/ssl/SSLContextService.java b/nifi/nifi-nar-bundles/nifi-standard-services/nifi-ssl-context-service-api/src/main/java/org/apache/nifi/ssl/SSLContextService.java index b2e3b765a5..13bab4ccde 100644 --- a/nifi/nifi-nar-bundles/nifi-standard-services/nifi-ssl-context-service-api/src/main/java/org/apache/nifi/ssl/SSLContextService.java +++ b/nifi/nifi-nar-bundles/nifi-standard-services/nifi-ssl-context-service-api/src/main/java/org/apache/nifi/ssl/SSLContextService.java @@ -57,4 +57,5 @@ public interface SSLContextService extends ControllerService { public boolean isKeyStoreConfigured(); + String getSslAlgorithm(); }