diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkSslClientContext.java b/handler/src/main/java/io/netty/handler/ssl/JdkSslClientContext.java index 60b65d554f..813e5020d9 100644 --- a/handler/src/main/java/io/netty/handler/ssl/JdkSslClientContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/JdkSslClientContext.java @@ -248,16 +248,17 @@ public final class JdkSslClientContext extends JdkSslContext { trustCertCollectionFile), trustManagerFactory, toX509CertificatesInternal(keyCertChainFile), toPrivateKeyInternal(keyFile, keyPassword), keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout), true, - ciphers, cipherFilter, apn, ClientAuth.NONE, false); + ciphers, cipherFilter, apn, ClientAuth.NONE, null, false); } JdkSslClientContext(X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory, X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, Iterable ciphers, CipherSuiteFilter cipherFilter, - ApplicationProtocolConfig apn, long sessionCacheSize, long sessionTimeout) throws SSLException { + ApplicationProtocolConfig apn, String[] protocols, long sessionCacheSize, long sessionTimeout) + throws SSLException { super(newSSLContext(trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout), true, - ciphers, cipherFilter, toNegotiator(apn, false), ClientAuth.NONE, false); + ciphers, cipherFilter, toNegotiator(apn, false), ClientAuth.NONE, protocols, false); } private static SSLContext newSSLContext(X509Certificate[] trustCertCollection, diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java b/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java index 4198c0c53a..d289ccb64a 100644 --- a/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java @@ -52,7 +52,7 @@ public class JdkSslContext extends SslContext { private static final InternalLogger logger = InternalLoggerFactory.getInstance(JdkSslContext.class); static final String PROTOCOL = "TLS"; - static final String[] PROTOCOLS; + static final String[] DEFAULT_PROTOCOLS; static final List DEFAULT_CIPHERS; static final Set SUPPORTED_CIPHERS; @@ -80,9 +80,9 @@ public class JdkSslContext extends SslContext { "TLSv1.2", "TLSv1.1", "TLSv1"); if (!protocols.isEmpty()) { - PROTOCOLS = protocols.toArray(new String[protocols.size()]); + DEFAULT_PROTOCOLS = protocols.toArray(new String[protocols.size()]); } else { - PROTOCOLS = engine.getEnabledProtocols(); + DEFAULT_PROTOCOLS = engine.getEnabledProtocols(); } // Choose the sensible default list of cipher suites. @@ -120,7 +120,7 @@ public class JdkSslContext extends SslContext { DEFAULT_CIPHERS = Collections.unmodifiableList(ciphers); if (logger.isDebugEnabled()) { - logger.debug("Default protocols (JDK): {} ", Arrays.asList(PROTOCOLS)); + logger.debug("Default protocols (JDK): {} ", Arrays.asList(DEFAULT_PROTOCOLS)); logger.debug("Default cipher suites (JDK): {}", DEFAULT_CIPHERS); } } @@ -133,6 +133,7 @@ public class JdkSslContext extends SslContext { } } + private final String[] protocols; private final String[] cipherSuites; private final List unmodifiableCipherSuites; private final JdkApplicationProtocolNegotiator apn; @@ -150,7 +151,7 @@ public class JdkSslContext extends SslContext { public JdkSslContext(SSLContext sslContext, boolean isClient, ClientAuth clientAuth) { this(sslContext, isClient, null, IdentityCipherSuiteFilter.INSTANCE, - JdkDefaultApplicationProtocolNegotiator.INSTANCE, clientAuth, false); + JdkDefaultApplicationProtocolNegotiator.INSTANCE, clientAuth, null, false); } /** @@ -166,16 +167,17 @@ public class JdkSslContext extends SslContext { public JdkSslContext(SSLContext sslContext, boolean isClient, Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, ClientAuth clientAuth) { - this(sslContext, isClient, ciphers, cipherFilter, toNegotiator(apn, !isClient), clientAuth, false); + this(sslContext, isClient, ciphers, cipherFilter, toNegotiator(apn, !isClient), clientAuth, null, false); } JdkSslContext(SSLContext sslContext, boolean isClient, Iterable ciphers, CipherSuiteFilter cipherFilter, - JdkApplicationProtocolNegotiator apn, ClientAuth clientAuth, boolean startTls) { + JdkApplicationProtocolNegotiator apn, ClientAuth clientAuth, String[] protocols, boolean startTls) { super(startTls); this.apn = checkNotNull(apn, "apn"); this.clientAuth = checkNotNull(clientAuth, "clientAuth"); cipherSuites = checkNotNull(cipherFilter, "cipherFilter").filterCipherSuites( ciphers, DEFAULT_CIPHERS, SUPPORTED_CIPHERS); + this.protocols = protocols == null ? DEFAULT_PROTOCOLS : protocols; unmodifiableCipherSuites = Collections.unmodifiableList(Arrays.asList(cipherSuites)); this.sslContext = checkNotNull(sslContext, "sslContext"); this.isClient = isClient; @@ -232,7 +234,7 @@ public class JdkSslContext extends SslContext { private SSLEngine configureAndWrapEngine(SSLEngine engine) { engine.setEnabledCipherSuites(cipherSuites); - engine.setEnabledProtocols(PROTOCOLS); + engine.setEnabledProtocols(protocols); engine.setUseClientMode(isClient()); if (isServer()) { switch (clientAuth) { diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkSslServerContext.java b/handler/src/main/java/io/netty/handler/ssl/JdkSslServerContext.java index e75f07553b..426771757e 100644 --- a/handler/src/main/java/io/netty/handler/ssl/JdkSslServerContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/JdkSslServerContext.java @@ -215,17 +215,17 @@ public final class JdkSslServerContext extends JdkSslContext { super(newSSLContext(toX509CertificatesInternal(trustCertCollectionFile), trustManagerFactory, toX509CertificatesInternal(keyCertChainFile), toPrivateKeyInternal(keyFile, keyPassword), keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout), false, - ciphers, cipherFilter, apn, ClientAuth.NONE, false); + ciphers, cipherFilter, apn, ClientAuth.NONE, null, false); } JdkSslServerContext(X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory, X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, long sessionCacheSize, long sessionTimeout, - ClientAuth clientAuth, boolean startTls) throws SSLException { + ClientAuth clientAuth, String[] protocols, boolean startTls) throws SSLException { super(newSSLContext(trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout), false, - ciphers, cipherFilter, toNegotiator(apn, true), clientAuth, startTls); + ciphers, cipherFilter, toNegotiator(apn, true), clientAuth, protocols, startTls); } private static SSLContext newSSLContext(X509Certificate[] trustCertCollection, diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java index a14a907bce..0e1902d8a3 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java @@ -175,17 +175,17 @@ public final class OpenSslClientContext extends OpenSslContext { throws SSLException { this(toX509CertificatesInternal(trustCertCollectionFile), trustManagerFactory, toX509CertificatesInternal(keyCertChainFile), toPrivateKeyInternal(keyFile, keyPassword), - keyPassword, keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout); + keyPassword, keyManagerFactory, ciphers, cipherFilter, apn, null, sessionCacheSize, sessionTimeout); } OpenSslClientContext(X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory, X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, Iterable ciphers, - CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, + CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, String[] protocols, long sessionCacheSize, long sessionTimeout) throws SSLException { super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_CLIENT, keyCertChain, - ClientAuth.NONE, false); + ClientAuth.NONE, protocols, false); boolean success = false; try { sessionContext = newSessionContext(this, ctx, engineMap, trustCertCollection, trustManagerFactory, diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java index 4cafbfb8f9..4523b330a5 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java @@ -29,18 +29,18 @@ import javax.net.ssl.SSLException; public abstract class OpenSslContext extends ReferenceCountedOpenSslContext { OpenSslContext(Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apnCfg, long sessionCacheSize, long sessionTimeout, int mode, Certificate[] keyCertChain, - ClientAuth clientAuth, boolean startTls) + ClientAuth clientAuth, String[] protocols, boolean startTls) throws SSLException { super(ciphers, cipherFilter, apnCfg, sessionCacheSize, sessionTimeout, mode, keyCertChain, - clientAuth, startTls, false); + clientAuth, protocols, startTls, false); } OpenSslContext(Iterable ciphers, CipherSuiteFilter cipherFilter, OpenSslApplicationProtocolNegotiator apn, long sessionCacheSize, long sessionTimeout, int mode, Certificate[] keyCertChain, - ClientAuth clientAuth, boolean startTls) throws SSLException { - super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, mode, keyCertChain, clientAuth, startTls, - false); + ClientAuth clientAuth, String[] protocols, boolean startTls) throws SSLException { + super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, mode, keyCertChain, clientAuth, protocols, + startTls, false); } @Override diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java index 9f98ddde10..b8b8a0f822 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java @@ -323,16 +323,17 @@ public final class OpenSslServerContext extends OpenSslContext { this(toX509CertificatesInternal(trustCertCollectionFile), trustManagerFactory, toX509CertificatesInternal(keyCertChainFile), toPrivateKeyInternal(keyFile, keyPassword), keyPassword, keyManagerFactory, ciphers, cipherFilter, - apn, sessionCacheSize, sessionTimeout, ClientAuth.NONE, false); + apn, sessionCacheSize, sessionTimeout, ClientAuth.NONE, null, false); } OpenSslServerContext( X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory, X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, - long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, boolean startTls) throws SSLException { + long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, String[] protocols, boolean startTls) + throws SSLException { this(trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, ciphers, - cipherFilter, toNegotiator(apn), sessionCacheSize, sessionTimeout, clientAuth, startTls); + cipherFilter, toNegotiator(apn), sessionCacheSize, sessionTimeout, clientAuth, protocols, startTls); } @SuppressWarnings("deprecation") @@ -340,9 +341,10 @@ public final class OpenSslServerContext extends OpenSslContext { X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory, X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, Iterable ciphers, CipherSuiteFilter cipherFilter, OpenSslApplicationProtocolNegotiator apn, - long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, boolean startTls) throws SSLException { + long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, String[] protocols, boolean startTls) + throws SSLException { super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_SERVER, keyCertChain, - clientAuth, startTls); + clientAuth, protocols, startTls); // Create a new SSL_CTX and configure it. boolean success = false; try { diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java index d989a4e05a..960b4e25cb 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java @@ -54,10 +54,10 @@ public final class ReferenceCountedOpenSslClientContext extends ReferenceCounted X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, - long sessionCacheSize, long sessionTimeout) + String[] protocols, long sessionCacheSize, long sessionTimeout) throws SSLException { super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_CLIENT, keyCertChain, - ClientAuth.NONE, false, true); + ClientAuth.NONE, protocols, false, true); boolean success = false; try { sessionContext = newSessionContext(this, ctx, engineMap, trustCertCollection, trustManagerFactory, diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java index b156694789..83c5af7120 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java @@ -140,6 +140,7 @@ public abstract class ReferenceCountedOpenSslContext extends SslContext implemen final Certificate[] keyCertChain; final ClientAuth clientAuth; + final String[] protocols; final OpenSslEngineMap engineMap = new DefaultOpenSslEngineMap(); private volatile boolean rejectRemoteInitiatedRenegotiation; private volatile int bioNonApplicationBufferSize = DEFAULT_BIO_NON_APPLICATION_BUFFER_SIZE; @@ -211,16 +212,17 @@ public abstract class ReferenceCountedOpenSslContext extends SslContext implemen ReferenceCountedOpenSslContext(Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apnCfg, long sessionCacheSize, long sessionTimeout, - int mode, Certificate[] keyCertChain, ClientAuth clientAuth, boolean startTls, - boolean leakDetection) throws SSLException { + int mode, Certificate[] keyCertChain, ClientAuth clientAuth, String[] protocols, + boolean startTls, boolean leakDetection) throws SSLException { this(ciphers, cipherFilter, toNegotiator(apnCfg), sessionCacheSize, sessionTimeout, mode, keyCertChain, - clientAuth, startTls, leakDetection); + clientAuth, protocols, startTls, leakDetection); } ReferenceCountedOpenSslContext(Iterable ciphers, CipherSuiteFilter cipherFilter, OpenSslApplicationProtocolNegotiator apn, long sessionCacheSize, long sessionTimeout, int mode, Certificate[] keyCertChain, - ClientAuth clientAuth, boolean startTls, boolean leakDetection) throws SSLException { + ClientAuth clientAuth, String[] protocols, boolean startTls, boolean leakDetection) + throws SSLException { super(startTls); OpenSsl.ensureAvailability(); @@ -231,6 +233,7 @@ public abstract class ReferenceCountedOpenSslContext extends SslContext implemen leak = leakDetection ? leakDetector.track(this) : null; this.mode = mode; this.clientAuth = isServer() ? checkNotNull(clientAuth, "clientAuth") : ClientAuth.NONE; + this.protocols = protocols; if (mode == SSL.SSL_MODE_SERVER) { rejectRemoteInitiatedRenegotiation = @@ -305,19 +308,19 @@ public abstract class ReferenceCountedOpenSslContext extends SslContext implemen List nextProtoList = apn.protocols(); /* Set next protocols for next protocol negotiation extension, if specified */ if (!nextProtoList.isEmpty()) { - String[] protocols = nextProtoList.toArray(new String[nextProtoList.size()]); + String[] appProtocols = nextProtoList.toArray(new String[nextProtoList.size()]); int selectorBehavior = opensslSelectorFailureBehavior(apn.selectorFailureBehavior()); switch (apn.protocol()) { case NPN: - SSLContext.setNpnProtos(ctx, protocols, selectorBehavior); + SSLContext.setNpnProtos(ctx, appProtocols, selectorBehavior); break; case ALPN: - SSLContext.setAlpnProtos(ctx, protocols, selectorBehavior); + SSLContext.setAlpnProtos(ctx, appProtocols, selectorBehavior); break; case NPN_AND_ALPN: - SSLContext.setNpnProtos(ctx, protocols, selectorBehavior); - SSLContext.setAlpnProtos(ctx, protocols, selectorBehavior); + SSLContext.setNpnProtos(ctx, appProtocols, selectorBehavior); + SSLContext.setAlpnProtos(ctx, appProtocols, selectorBehavior); break; default: throw new Error(); diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java index 679a57e9d6..7a6833ec5d 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java @@ -237,6 +237,10 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc // needed JNI methods. setClientAuth(clientMode ? ClientAuth.NONE : context.clientAuth); + if (context.protocols != null) { + setEnabledProtocols(context.protocols); + } + // Use SNI if peerHost was specified // See https://github.com/netty/netty/issues/4746 if (clientMode && peerHost != null) { diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java index 5023c52ea0..a367ed4c1f 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java @@ -49,18 +49,20 @@ public final class ReferenceCountedOpenSslServerContext extends ReferenceCounted X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory, X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, - long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, boolean startTls) throws SSLException { + long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, String[] protocols, boolean startTls) + throws SSLException { this(trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, ciphers, - cipherFilter, toNegotiator(apn), sessionCacheSize, sessionTimeout, clientAuth, startTls); + cipherFilter, toNegotiator(apn), sessionCacheSize, sessionTimeout, clientAuth, protocols, startTls); } private ReferenceCountedOpenSslServerContext( X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory, X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, Iterable ciphers, CipherSuiteFilter cipherFilter, OpenSslApplicationProtocolNegotiator apn, - long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, boolean startTls) throws SSLException { + long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, String[] protocols, boolean startTls) + throws SSLException { super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_SERVER, keyCertChain, - clientAuth, startTls, true); + clientAuth, protocols, startTls, true); // Create a new SSL_CTX and configure it. boolean success = false; try { diff --git a/handler/src/main/java/io/netty/handler/ssl/SslContext.java b/handler/src/main/java/io/netty/handler/ssl/SslContext.java index da6fa69972..e6ac26dfe0 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslContext.java @@ -384,7 +384,7 @@ public abstract class SslContext { toX509Certificates(keyCertChainFile), toPrivateKey(keyFile, keyPassword), keyPassword, keyManagerFactory, ciphers, cipherFilter, apn, - sessionCacheSize, sessionTimeout, ClientAuth.NONE, false); + sessionCacheSize, sessionTimeout, ClientAuth.NONE, null, false); } catch (Exception e) { if (e instanceof SSLException) { throw (SSLException) e; @@ -398,7 +398,8 @@ public abstract class SslContext { X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory, X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, - long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, boolean startTls) throws SSLException { + long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, String[] protocols, boolean startTls) + throws SSLException { if (provider == null) { provider = defaultServerProvider(); @@ -409,17 +410,17 @@ public abstract class SslContext { return new JdkSslServerContext( trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, - clientAuth, startTls); + clientAuth, protocols, startTls); case OPENSSL: return new OpenSslServerContext( trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, - clientAuth, startTls); + clientAuth, protocols, startTls); case OPENSSL_REFCNT: return new ReferenceCountedOpenSslServerContext( trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, - clientAuth, startTls); + clientAuth, protocols, startTls); default: throw new Error(provider.toString()); } @@ -727,8 +728,7 @@ public abstract class SslContext { return newClientContextInternal(provider, toX509Certificates(trustCertCollectionFile), trustManagerFactory, toX509Certificates(keyCertChainFile), toPrivateKey(keyFile, keyPassword), keyPassword, keyManagerFactory, ciphers, cipherFilter, - apn, - sessionCacheSize, sessionTimeout); + apn, null, sessionCacheSize, sessionTimeout); } catch (Exception e) { if (e instanceof SSLException) { throw (SSLException) e; @@ -741,7 +741,7 @@ public abstract class SslContext { SslProvider provider, X509Certificate[] trustCert, TrustManagerFactory trustManagerFactory, X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, - Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, + Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, String[] protocols, long sessionCacheSize, long sessionTimeout) throws SSLException { if (provider == null) { provider = defaultClientProvider(); @@ -750,15 +750,15 @@ public abstract class SslContext { case JDK: return new JdkSslClientContext( trustCert, trustManagerFactory, keyCertChain, key, keyPassword, - keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout); + keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout); case OPENSSL: return new OpenSslClientContext( trustCert, trustManagerFactory, keyCertChain, key, keyPassword, - keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout); + keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout); case OPENSSL_REFCNT: return new ReferenceCountedOpenSslClientContext( trustCert, trustManagerFactory, keyCertChain, key, keyPassword, - keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout); + keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout); default: throw new Error(provider.toString()); } diff --git a/handler/src/main/java/io/netty/handler/ssl/SslContextBuilder.java b/handler/src/main/java/io/netty/handler/ssl/SslContextBuilder.java index 0a5d7f5dc0..12f520077e 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslContextBuilder.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslContextBuilder.java @@ -16,15 +16,16 @@ package io.netty.handler.ssl; -import static io.netty.util.internal.ObjectUtil.checkNotNull; - -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLException; -import javax.net.ssl.TrustManagerFactory; import java.io.File; import java.io.InputStream; import java.security.PrivateKey; import java.security.cert.X509Certificate; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLException; +import javax.net.ssl.TrustManagerFactory; + +import static io.netty.util.internal.ObjectUtil.checkNotNull; /** * Builder for configuring a new SslContext for creation. @@ -137,6 +138,7 @@ public final class SslContextBuilder { private long sessionCacheSize; private long sessionTimeout; private ClientAuth clientAuth = ClientAuth.NONE; + private String[] protocols; private boolean startTls; private SslContextBuilder(boolean forServer) { @@ -384,6 +386,16 @@ public final class SslContextBuilder { return this; } + /** + * The TLS protocol versions to enable. + * @param protocols The protocols to enable, or {@code null} to enable the default protocols. + * @see SSLEngine#setEnabledCipherSuites(String[]) + */ + public SslContextBuilder protocols(String... protocols) { + this.protocols = protocols == null ? null : protocols.clone(); + return this; + } + /** * {@code true} if the first write request shouldn't be encrypted. */ @@ -401,11 +413,11 @@ public final class SslContextBuilder { if (forServer) { return SslContext.newServerContextInternal(provider, trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, - ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, clientAuth, startTls); + ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, clientAuth, protocols, startTls); } else { return SslContext.newClientContextInternal(provider, trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, - ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout); + ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout); } } } diff --git a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java index c64a00a6f2..1cfd1b8f1b 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java @@ -63,13 +63,11 @@ import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SNIHostName; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLException; @@ -1633,6 +1631,43 @@ public abstract class SSLEngineTest { } } + @Test + public void testProtocolMatch() throws Exception { + testProtocol(new String[] {"TLSv1.2"}, new String[] {"TLSv1", "TLSv1.1", "TLSv1.2"}); + } + + @Test(expected = SSLHandshakeException.class) + public void testProtocolNoMatch() throws Exception { + testProtocol(new String[] {"TLSv1.2"}, new String[] {"TLSv1", "TLSv1.1"}); + } + + private void testProtocol(String[] clientProtocols, String[] serverProtocols) throws Exception { + SelfSignedCertificate cert = new SelfSignedCertificate(); + + clientSslCtx = SslContextBuilder + .forClient() + .trustManager(cert.cert()) + .sslProvider(sslClientProvider()) + .protocols(clientProtocols) + .build(); + SSLEngine client = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + + serverSslCtx = SslContextBuilder + .forServer(cert.certificate(), cert.privateKey()) + .sslProvider(sslServerProvider()) + .protocols(serverProtocols) + .build(); + SSLEngine server = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + + try { + handshake(client, server); + } finally { + cleanupClientSslEngine(client); + cleanupServerSslEngine(server); + cert.delete(); + } + } + @Test public void testPacketBufferSizeLimit() throws Exception { SelfSignedCertificate cert = new SelfSignedCertificate();