From 6501c3de1fc8d440923b241f95a4f7f0ead6a7c3 Mon Sep 17 00:00:00 2001 From: "rico.pahlisch" Date: Mon, 5 Feb 2018 10:23:18 +0100 Subject: [PATCH] ARTEMIS-1649 - enable openssl provider for Netty --- .../remoting/impl/netty/NettyConnector.java | 175 ++++++++++-------- .../impl/netty/TransportConstants.java | 8 + .../core/remoting/impl/ssl/SSLSupport.java | 82 +++++--- .../remoting/impl/netty/NettyAcceptor.java | 106 ++++++++--- docs/user-manual/en/configuring-transports.md | 10 + 5 files changed, 253 insertions(+), 128 deletions(-) diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyConnector.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyConnector.java index 5874820d20..99d84479c4 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyConnector.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyConnector.java @@ -33,6 +33,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch; @@ -40,6 +41,7 @@ import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; @@ -49,6 +51,7 @@ import javax.security.auth.login.LoginContext; import io.netty.bootstrap.Bootstrap; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelDuplexHandler; @@ -90,13 +93,13 @@ import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.LastHttpContent; import io.netty.handler.codec.http.cookie.ClientCookieDecoder; import io.netty.handler.codec.http.cookie.Cookie; +import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslHandler; import io.netty.util.AttributeKey; import io.netty.util.ResourceLeakDetector; import io.netty.util.ResourceLeakDetector.Level; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GlobalEventExecutor; - import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration; import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.core.client.ActiveMQClientLogger; @@ -216,6 +219,8 @@ public class NettyConnector extends AbstractConnector { private String enabledProtocols; + private String sslProvider; + private boolean verifyHost; private boolean trustAll; @@ -354,6 +359,8 @@ public class NettyConnector extends AbstractConnector { trustAll = ConfigurationHelper.getBooleanProperty(TransportConstants.TRUST_ALL_PROP_NAME, TransportConstants.DEFAULT_TRUST_ALL, configuration); + sslProvider = ConfigurationHelper.getStringProperty(TransportConstants.SSL_PROVIDER, TransportConstants.DEFAULT_SSL_PROVIDER, configuration); + sniHost = ConfigurationHelper.getStringProperty(TransportConstants.SNIHOST_PROP_NAME, TransportConstants.DEFAULT_SNIHOST_CONFIG, configuration); kerb5Config = ConfigurationHelper.getStringProperty(TransportConstants.SSL_KRB5_CONFIG_PROP_NAME, TransportConstants.DEFAULT_SSL_KRB5_CONFIG, configuration); @@ -484,67 +491,30 @@ public class NettyConnector extends AbstractConnector { bootstrap.option(ChannelOption.SO_REUSEADDR, true); channelGroup = new DefaultChannelGroup("activemq-connector", GlobalEventExecutor.INSTANCE); - final SSLContext context; + final String realKeyStorePath; + final String realKeyStoreProvider; + final String realKeyStorePassword; + final String realTrustStorePath; + final String realTrustStoreProvider; + final String realTrustStorePassword; + if (sslEnabled) { - try { - if (useDefaultSslContext) { - context = SSLContext.getDefault(); - } else { - // HORNETQ-680 - override the server-side config if client-side system properties are set - String realKeyStorePath = keyStorePath; - String realKeyStoreProvider = keyStoreProvider; - String realKeyStorePassword = keyStorePassword; - if (System.getProperty(JAVAX_KEYSTORE_PATH_PROP_NAME) != null) { - realKeyStorePath = System.getProperty(JAVAX_KEYSTORE_PATH_PROP_NAME); - } - if (System.getProperty(JAVAX_KEYSTORE_PASSWORD_PROP_NAME) != null) { - realKeyStorePassword = System.getProperty(JAVAX_KEYSTORE_PASSWORD_PROP_NAME); - } + // HORNETQ-680 - override the server-side config if client-side system properties are set - if (System.getProperty(ACTIVEMQ_KEYSTORE_PROVIDER_PROP_NAME) != null) { - realKeyStoreProvider = System.getProperty(ACTIVEMQ_KEYSTORE_PROVIDER_PROP_NAME); - } - if (System.getProperty(ACTIVEMQ_KEYSTORE_PATH_PROP_NAME) != null) { - realKeyStorePath = System.getProperty(ACTIVEMQ_KEYSTORE_PATH_PROP_NAME); - } - if (System.getProperty(ACTIVEMQ_KEYSTORE_PASSWORD_PROP_NAME) != null) { - realKeyStorePassword = System.getProperty(ACTIVEMQ_KEYSTORE_PASSWORD_PROP_NAME); - } + realKeyStorePath = Stream.of(System.getProperty(JAVAX_KEYSTORE_PATH_PROP_NAME), System.getProperty(ACTIVEMQ_KEYSTORE_PATH_PROP_NAME), keyStorePath).map(v -> useDefaultSslContext ? keyStorePath : v).filter(Objects::nonNull).findFirst().orElse(null); + realKeyStorePassword = Stream.of(System.getProperty(JAVAX_KEYSTORE_PASSWORD_PROP_NAME), System.getProperty(ACTIVEMQ_KEYSTORE_PASSWORD_PROP_NAME), keyStorePassword).map(v -> useDefaultSslContext ? keyStorePassword : v).filter(Objects::nonNull).findFirst().orElse(null); + realKeyStoreProvider = Stream.of(System.getProperty(ACTIVEMQ_KEYSTORE_PROVIDER_PROP_NAME), keyStoreProvider).map(v -> useDefaultSslContext ? keyStoreProvider : v).filter(Objects::nonNull).findFirst().orElse(null); - String realTrustStorePath = trustStorePath; - String realTrustStoreProvider = trustStoreProvider; - String realTrustStorePassword = trustStorePassword; - if (System.getProperty(JAVAX_TRUSTSTORE_PATH_PROP_NAME) != null) { - realTrustStorePath = System.getProperty(JAVAX_TRUSTSTORE_PATH_PROP_NAME); - } - if (System.getProperty(JAVAX_TRUSTSTORE_PASSWORD_PROP_NAME) != null) { - realTrustStorePassword = System.getProperty(JAVAX_TRUSTSTORE_PASSWORD_PROP_NAME); - } - - if (System.getProperty(ACTIVEMQ_TRUSTSTORE_PROVIDER_PROP_NAME) != null) { - realTrustStoreProvider = System.getProperty(ACTIVEMQ_TRUSTSTORE_PROVIDER_PROP_NAME); - } - if (System.getProperty(ACTIVEMQ_TRUSTSTORE_PATH_PROP_NAME) != null) { - realTrustStorePath = System.getProperty(ACTIVEMQ_TRUSTSTORE_PATH_PROP_NAME); - } - if (System.getProperty(ACTIVEMQ_TRUSTSTORE_PASSWORD_PROP_NAME) != null) { - realTrustStorePassword = System.getProperty(ACTIVEMQ_TRUSTSTORE_PASSWORD_PROP_NAME); - } - context = SSLSupport.createContext(realKeyStoreProvider, realKeyStorePath, realKeyStorePassword, realTrustStoreProvider, realTrustStorePath, realTrustStorePassword, trustAll, crlPath); - } - } catch (Exception e) { - close(); - IllegalStateException ise = new IllegalStateException("Unable to create NettyConnector for " + host + ":" + port); - ise.initCause(e); - throw ise; - } + realTrustStorePath = Stream.of(System.getProperty(JAVAX_TRUSTSTORE_PATH_PROP_NAME), System.getProperty(ACTIVEMQ_TRUSTSTORE_PATH_PROP_NAME), trustStorePath).map(v -> useDefaultSslContext ? trustStorePath : v).filter(Objects::nonNull).findFirst().orElse(null); + realTrustStorePassword = Stream.of(System.getProperty(JAVAX_TRUSTSTORE_PASSWORD_PROP_NAME), System.getProperty(ACTIVEMQ_TRUSTSTORE_PASSWORD_PROP_NAME), trustStorePassword).map(v -> useDefaultSslContext ? trustStorePassword : v).filter(Objects::nonNull).findFirst().orElse(null); + realTrustStoreProvider = Stream.of(System.getProperty(ACTIVEMQ_TRUSTSTORE_PROVIDER_PROP_NAME), trustStoreProvider).map(v -> useDefaultSslContext ? trustStoreProvider : v).filter(Objects::nonNull).findFirst().orElse(null); } else { - context = null; // Unused - } - - if (context != null && useServlet) { - // TODO: Fix me - //bootstrap.setOption("sslContext", context); + realKeyStorePath = null; + realKeyStoreProvider = null; + realKeyStorePassword = null; + realTrustStorePath = null; + realTrustStoreProvider = null; + realTrustStorePassword = null; } bootstrap.handler(new ChannelInitializer() { @@ -553,25 +523,13 @@ public class NettyConnector extends AbstractConnector { final ChannelPipeline pipeline = channel.pipeline(); if (sslEnabled && !useServlet) { - Subject subject = null; - if (kerb5Config != null) { - LoginContext loginContext = new LoginContext(kerb5Config); - loginContext.login(); - subject = loginContext.getSubject(); - verifyHost = true; + SSLEngine engine; + if (sslProvider.equals(TransportConstants.OPENSSL_PROVIDER)) { + engine = loadOpenSslEngine(channel.alloc(), realKeyStoreProvider, realKeyStorePath, realKeyStorePassword, realTrustStoreProvider, realTrustStorePath, realTrustStorePassword); + } else { + engine = loadJdkSslEngine(useDefaultSslContext, realKeyStoreProvider, realKeyStorePath, realKeyStorePassword, realTrustStoreProvider, realTrustStorePath, realTrustStorePassword); } - SSLEngine engine = Subject.doAs(subject, new PrivilegedExceptionAction() { - @Override - public SSLEngine run() { - if (verifyHost) { - return context.createSSLEngine(sniHost != null ? sniHost : host, port); - } else { - return context.createSSLEngine(); - } - } - }); - engine.setUseClientMode(true); engine.setWantClientAuth(true); @@ -640,7 +598,72 @@ public class NettyConnector extends AbstractConnector { batchFlusherFuture = scheduledThreadPool.scheduleWithFixedDelay(flusher, batchDelay, batchDelay, TimeUnit.MILLISECONDS); } - ActiveMQClientLogger.LOGGER.startedNettyConnector(connectorType, TransportConstants.NETTY_VERSION, host, port); + ActiveMQClientLogger.LOGGER.startedNettyConnector(connectorType, TransportConstants.NETTY_VERSION, host, port); + } + + private SSLEngine loadJdkSslEngine(boolean useDefaultSslContext, + String realKeyStoreProvider, + String realKeyStorePath, + String realKeyStorePassword, + String realTrustStoreProvider, + String realTrustStorePath, + String realTrustStorePassword) throws Exception { + SSLContext context; + if (useDefaultSslContext) { + context = SSLContext.getDefault(); + } else { + context = SSLSupport.createContext(realKeyStoreProvider, realKeyStorePath, realKeyStorePassword, realTrustStoreProvider, realTrustStorePath, realTrustStorePassword, trustAll, crlPath); + } + Subject subject = null; + if (kerb5Config != null) { + LoginContext loginContext = new LoginContext(kerb5Config); + loginContext.login(); + subject = loginContext.getSubject(); + verifyHost = true; + } + + SSLEngine engine = Subject.doAs(subject, new PrivilegedExceptionAction() { + @Override + public SSLEngine run() { + if (verifyHost) { + return context.createSSLEngine(host, port); + } else { + return context.createSSLEngine(); + } + } + }); + return engine; + } + + private SSLEngine loadOpenSslEngine(ByteBufAllocator alloc, + String realKeyStoreProvider, + String realKeyStorePath, + String realKeyStorePassword, + String realTrustStoreProvider, + String realTrustStorePath, + String realTrustStorePassword) throws Exception { + + SslContext context = SSLSupport.createNettyContext(realKeyStoreProvider, realKeyStorePath, realKeyStorePassword, realTrustStoreProvider, realTrustStorePath, realTrustStorePassword, sslProvider); + + Subject subject = null; + if (kerb5Config != null) { + LoginContext loginContext = new LoginContext(kerb5Config); + loginContext.login(); + subject = loginContext.getSubject(); + verifyHost = true; + } + + SSLEngine engine = Subject.doAs(subject, new PrivilegedExceptionAction() { + @Override + public SSLEngine run() { + if (verifyHost) { + return context.newEngine(alloc, sniHost != null ? sniHost : host, port); + } else { + return context.newEngine(alloc); + } + } + }); + return engine; } @Override diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/TransportConstants.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/TransportConstants.java index 81efb31b32..d8a88545d3 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/TransportConstants.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/TransportConstants.java @@ -113,6 +113,8 @@ public class TransportConstants { public static final String USE_DEFAULT_SSL_CONTEXT_PROP_NAME = "useDefaultSslContext"; + public static final String SSL_PROVIDER = "sslProvider"; + public static final String NETTY_VERSION; /** @@ -201,6 +203,10 @@ public class TransportConstants { public static final boolean DEFAULT_VERIFY_HOST = false; + public static final String DEFAULT_SSL_PROVIDER = "JDK"; + + public static final String OPENSSL_PROVIDER = "OPENSSL"; + public static final boolean DEFAULT_TRUST_ALL = false; public static final boolean DEFAULT_USE_DEFAULT_SSL_CONTEXT = false; @@ -316,6 +322,7 @@ public class TransportConstants { allowableAcceptorKeys.add(TransportConstants.BACKLOG_PROP_NAME); allowableAcceptorKeys.add(TransportConstants.CRL_PATH_PROP_NAME); allowableAcceptorKeys.add(TransportConstants.HANDSHAKE_TIMEOUT); + allowableAcceptorKeys.add(TransportConstants.SSL_PROVIDER); ALLOWABLE_ACCEPTOR_KEYS = Collections.unmodifiableSet(allowableAcceptorKeys); @@ -361,6 +368,7 @@ public class TransportConstants { allowableConnectorKeys.add(ActiveMQDefaultConfiguration.getPropPasswordCodec()); allowableConnectorKeys.add(TransportConstants.NETTY_CONNECT_TIMEOUT); allowableConnectorKeys.add(TransportConstants.USE_DEFAULT_SSL_CONTEXT_PROP_NAME); + allowableConnectorKeys.add(TransportConstants.SSL_PROVIDER); allowableConnectorKeys.add(TransportConstants.HANDSHAKE_TIMEOUT); allowableConnectorKeys.add(TransportConstants.CRL_PATH_PROP_NAME); diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/ssl/SSLSupport.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/ssl/SSLSupport.java index 03b6e08217..297b294ec6 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/ssl/SSLSupport.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/ssl/SSLSupport.java @@ -16,14 +16,6 @@ */ package org.apache.activemq.artemis.core.remoting.impl.ssl; -import java.security.Security; -import java.security.cert.CRL; -import java.security.cert.CertStore; -import java.security.cert.CertificateFactory; -import java.security.cert.CollectionCertStoreParameters; -import java.security.cert.PKIXBuilderParameters; -import java.security.cert.X509CertSelector; -import java.util.Collection; import javax.net.ssl.CertPathTrustManagerParameters; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; @@ -37,12 +29,24 @@ import java.net.MalformedURLException; import java.net.URL; import java.security.AccessController; import java.security.KeyStore; +import java.security.PrivateKey; import java.security.PrivilegedAction; import java.security.SecureRandom; -import org.apache.activemq.artemis.utils.ClassloadingUtil; +import java.security.Security; +import java.security.cert.CRL; +import java.security.cert.CertStore; +import java.security.cert.CertificateFactory; +import java.security.cert.CollectionCertStoreParameters; +import java.security.cert.PKIXBuilderParameters; +import java.security.cert.X509CertSelector; +import java.security.cert.X509Certificate; +import java.util.Collection; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; +import io.netty.handler.ssl.SslProvider; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; - +import org.apache.activemq.artemis.utils.ClassloadingUtil; /** * Please note, this class supports PKCS#11 keystores, but there are no specific tests in the ActiveMQ Artemis test-suite to @@ -99,6 +103,21 @@ public class SSLSupport { return context; } + public static SslContext createNettyContext(final String keystoreProvider, + final String keystorePath, + final String keystorePassword, + final String trustStoreProvider, + final String trustStorePath, + final String trustStorePassword, + final String sslProvider) throws Exception { + + KeyStore keyStore = SSLSupport.loadKeystore(keystoreProvider, keystorePath, keystorePassword); + String alias = keyStore.aliases().nextElement(); + PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, keystorePassword.toCharArray()); + X509Certificate certificate = (X509Certificate) keyStore.getCertificate(alias); + return SslContextBuilder.forServer(privateKey, certificate).sslProvider(SslProvider.valueOf(sslProvider)).trustManager(SSLSupport.loadTrustManagerFactory(trustStoreProvider, trustStorePath, trustStorePassword, false, null)).build(); + } + public static String[] parseCommaSeparatedListIntoArray(String suites) { String[] cipherSuites = suites.split(","); for (int i = 0; i < cipherSuites.length; i++) { @@ -120,15 +139,14 @@ public class SSLSupport { } // Private ------------------------------------------------------- - - private static TrustManager[] loadTrustManager(final String trustStoreProvider, - final String trustStorePath, - final String trustStorePassword, - final boolean trustAll, - final String crlPath) throws Exception { + private static TrustManagerFactory loadTrustManagerFactory(final String trustStoreProvider, + final String trustStorePath, + final String trustStorePassword, + final boolean trustAll, + final String crlPath) throws Exception { if (trustAll) { //This is useful for testing but not should be used outside of that purpose - return InsecureTrustManagerFactory.INSTANCE.getTrustManagers(); + return InsecureTrustManagerFactory.INSTANCE; } else if (trustStorePath == null && (trustStoreProvider == null || !"PKCS11".equals(trustStoreProvider.toUpperCase()))) { return null; } else { @@ -153,12 +171,22 @@ public class SSLSupport { if (!initialized) { trustMgrFactory.init(trustStore); } - - return trustMgrFactory.getTrustManagers(); - + return trustMgrFactory; } } + private static TrustManager[] loadTrustManager(final String trustStoreProvider, + final String trustStorePath, + final String trustStorePassword, + final boolean trustAll, + final String crlPath) throws Exception { + TrustManagerFactory trustManagerFactory = loadTrustManagerFactory(trustStoreProvider, trustStorePath, trustStorePassword, trustAll, crlPath); + if (trustManagerFactory == null) { + return null; + } + return trustManagerFactory.getTrustManagers(); + } + private static Collection loadCRL(String crlPath) throws Exception { if (crlPath == null) { return null; @@ -196,14 +224,24 @@ public class SSLSupport { private static KeyManager[] loadKeyManagers(final String keyStoreProvider, final String keystorePath, final String keystorePassword) throws Exception { + + KeyManagerFactory factory = loadKeyManagerFactory(keyStoreProvider, keystorePath, keystorePassword); + if (factory == null) { + return null; + } + return factory.getKeyManagers(); + } + + private static KeyManagerFactory loadKeyManagerFactory(final String keyStoreProvider, + final String keystorePath, + final String keystorePassword) throws Exception { if (keystorePath == null && (keyStoreProvider == null || !"PKCS11".equals(keyStoreProvider.toUpperCase()))) { return null; } else { KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); KeyStore ks = SSLSupport.loadKeystore(keyStoreProvider, keystorePath, keystorePassword); kmf.init(ks, keystorePassword == null ? null : keystorePassword.toCharArray()); - - return kmf.getKeyManagers(); + return kmf; } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyAcceptor.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyAcceptor.java index 52c5b7ea46..f6424e3c08 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyAcceptor.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyAcceptor.java @@ -40,6 +40,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import io.netty.bootstrap.ServerBootstrap; +import io.netty.buffer.ByteBufAllocator; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; @@ -63,6 +64,7 @@ import io.netty.channel.local.LocalAddress; import io.netty.channel.local.LocalServerChannel; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslHandler; import io.netty.util.ResourceLeakDetector; import io.netty.util.concurrent.GenericFutureListener; @@ -164,6 +166,8 @@ public class NettyAcceptor extends AbstractAcceptor { private final boolean needClientAuth; + private final String sslProvider; + private final boolean verifyHost; private final String kerb5Config; @@ -270,6 +274,8 @@ public class NettyAcceptor extends AbstractAcceptor { needClientAuth = ConfigurationHelper.getBooleanProperty(TransportConstants.NEED_CLIENT_AUTH_PROP_NAME, TransportConstants.DEFAULT_NEED_CLIENT_AUTH, configuration); verifyHost = ConfigurationHelper.getBooleanProperty(TransportConstants.VERIFY_HOST_PROP_NAME, TransportConstants.DEFAULT_VERIFY_HOST, configuration); + + sslProvider = ConfigurationHelper.getStringProperty(TransportConstants.SSL_PROVIDER, TransportConstants.DEFAULT_SSL_PROVIDER, configuration); } else { keyStoreProvider = TransportConstants.DEFAULT_KEYSTORE_PROVIDER; keyStorePath = TransportConstants.DEFAULT_KEYSTORE_PATH; @@ -282,6 +288,7 @@ public class NettyAcceptor extends AbstractAcceptor { enabledProtocols = TransportConstants.DEFAULT_ENABLED_PROTOCOLS; needClientAuth = TransportConstants.DEFAULT_NEED_CLIENT_AUTH; verifyHost = TransportConstants.DEFAULT_VERIFY_HOST; + sslProvider = TransportConstants.DEFAULT_SSL_PROVIDER; } tcpNoDelay = ConfigurationHelper.getBooleanProperty(TransportConstants.TCP_NODELAY_PROPNAME, TransportConstants.DEFAULT_TCP_NODELAY, configuration); @@ -364,7 +371,7 @@ public class NettyAcceptor extends AbstractAcceptor { public void initChannel(Channel channel) throws Exception { ChannelPipeline pipeline = channel.pipeline(); if (sslEnabled) { - pipeline.addLast("ssl", getSslHandler()); + pipeline.addLast("ssl", getSslHandler(channel.alloc())); pipeline.addLast("sslHandshakeExceptionHandler", new SslHandshakeExceptionHandler()); } pipeline.addLast(protocolHandler.getProtocolDecoder()); @@ -451,36 +458,13 @@ public class NettyAcceptor extends AbstractAcceptor { startServerChannels(); } - public synchronized SslHandler getSslHandler() throws Exception { - final SSLContext context; - try { - if (kerb5Config == null && keyStorePath == null && TransportConstants.DEFAULT_TRUSTSTORE_PROVIDER.equals(keyStoreProvider)) - throw new IllegalArgumentException("If \"" + TransportConstants.SSL_ENABLED_PROP_NAME + - "\" is true then \"" + TransportConstants.KEYSTORE_PATH_PROP_NAME + "\" must be non-null " + - "unless an alternative \"" + TransportConstants.KEYSTORE_PROVIDER_PROP_NAME + "\" has been specified."); - context = SSLSupport.createContext(keyStoreProvider, keyStorePath, keyStorePassword, trustStoreProvider, trustStorePath, trustStorePassword, crlPath); - } catch (Exception e) { - IllegalStateException ise = new IllegalStateException("Unable to create NettyAcceptor for " + host + ":" + port); - ise.initCause(e); - throw ise; + public synchronized SslHandler getSslHandler(ByteBufAllocator alloc) throws Exception { + SSLEngine engine; + if (sslProvider.equals(TransportConstants.OPENSSL_PROVIDER)) { + engine = loadOpenSslEngine(alloc); + } else { + engine = loadJdkSslEngine(); } - Subject subject = null; - if (kerb5Config != null) { - LoginContext loginContext = new LoginContext(kerb5Config); - loginContext.login(); - subject = loginContext.getSubject(); - } - - SSLEngine engine = Subject.doAs(subject, new PrivilegedExceptionAction() { - @Override - public SSLEngine run() { - if (verifyHost) { - return context.createSSLEngine(host, port); - } else { - return context.createSSLEngine(); - } - } - }); engine.setUseClientMode(false); @@ -539,6 +523,68 @@ public class NettyAcceptor extends AbstractAcceptor { return new SslHandler(engine); } + private SSLEngine loadJdkSslEngine() throws Exception { + final SSLContext context; + try { + if (kerb5Config == null && keyStorePath == null && TransportConstants.DEFAULT_TRUSTSTORE_PROVIDER.equals(keyStoreProvider)) + throw new IllegalArgumentException("If \"" + TransportConstants.SSL_ENABLED_PROP_NAME + "\" is true then \"" + TransportConstants.KEYSTORE_PATH_PROP_NAME + "\" must be non-null " + "unless an alternative \"" + TransportConstants.KEYSTORE_PROVIDER_PROP_NAME + "\" has been specified."); + context = SSLSupport.createContext(keyStoreProvider, keyStorePath, keyStorePassword, trustStoreProvider, trustStorePath, trustStorePassword, crlPath); + } catch (Exception e) { + IllegalStateException ise = new IllegalStateException("Unable to create NettyAcceptor for " + host + ":" + port); + ise.initCause(e); + throw ise; + } + Subject subject = null; + if (kerb5Config != null) { + LoginContext loginContext = new LoginContext(kerb5Config); + loginContext.login(); + subject = loginContext.getSubject(); + } + + SSLEngine engine = Subject.doAs(subject, new PrivilegedExceptionAction() { + @Override + public SSLEngine run() { + if (verifyHost) { + return context.createSSLEngine(host, port); + } else { + return context.createSSLEngine(); + } + } + }); + return engine; + } + + private SSLEngine loadOpenSslEngine(ByteBufAllocator alloc) throws Exception { + final SslContext context; + try { + if (kerb5Config == null && keyStorePath == null && TransportConstants.DEFAULT_TRUSTSTORE_PROVIDER.equals(keyStoreProvider)) + throw new IllegalArgumentException("If \"" + TransportConstants.SSL_ENABLED_PROP_NAME + "\" is true then \"" + TransportConstants.KEYSTORE_PATH_PROP_NAME + "\" must be non-null " + "unless an alternative \"" + TransportConstants.KEYSTORE_PROVIDER_PROP_NAME + "\" has been specified."); + context = SSLSupport.createNettyContext(keyStoreProvider, keyStorePath, keyStorePassword, trustStoreProvider, trustStorePath, trustStorePassword, sslProvider); + } catch (Exception e) { + IllegalStateException ise = new IllegalStateException("Unable to create NettyAcceptor for " + host + ":" + port); + ise.initCause(e); + throw ise; + } + Subject subject = null; + if (kerb5Config != null) { + LoginContext loginContext = new LoginContext(kerb5Config); + loginContext.login(); + subject = loginContext.getSubject(); + } + + SSLEngine engine = Subject.doAs(subject, new PrivilegedExceptionAction() { + @Override + public SSLEngine run() { + if (verifyHost) { + return context.newEngine(alloc, host, port); + } else { + return context.newEngine(alloc); + } + } + }); + return engine; + } + private void startServerChannels() { String[] hosts = TransportConfiguration.splitHosts(host); for (String h : hosts) { diff --git a/docs/user-manual/en/configuring-transports.md b/docs/user-manual/en/configuring-transports.md index 566f69f518..5f83627633 100644 --- a/docs/user-manual/en/configuring-transports.md +++ b/docs/user-manual/en/configuring-transports.md @@ -433,6 +433,16 @@ following additional properties: Valid values are `true` or `false`. Default is `false`. +- `sslProvider` + + Used to change the SSL Provider between `JDK` and `OPENSSL`. The default is `JDK`. + If used with `OPENSSL` you can add `netty-tcnative` to your classpath to use the native + installed openssl. This can be useful if you want to use special ciphersuite - elliptic curve combinations + which are support through openssl but not through the JDK provider. See https://en.wikipedia.org/wiki/Comparison_of_TLS_implementations + for more information's. + + + ## Configuring Netty HTTP Netty HTTP tunnels packets over the HTTP protocol. It can be useful in