Add support for TLSv1.3 (#8293)

Motivation:

TLSv1.3 support is included in java11 and is also supported by OpenSSL 1.1.1, so we should support when possible.

Modifications:
- Add support for TLSv1.3 using either the JDK implementation or the native implementation provided by netty-tcnative when compiled against openssl 1.1.1
- Adjust unit tests for semantics provided by TLSv1.3
- Correctly handle custom Provider implementations that not support TLSv1.3

Result:

Be able to use TLSv1.3 with netty.
This commit is contained in:
Norman Maurer 2018-10-17 08:35:35 +02:00 committed by GitHub
parent 9eebe7ed74
commit 0ddc62cec0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 866 additions and 331 deletions

View File

@ -122,33 +122,6 @@ final class CipherSuiteConverter {
} }
} }
/**
* Converts the specified Java cipher suites to the colon-separated OpenSSL cipher suite specification.
*/
static String toOpenSsl(Iterable<String> javaCipherSuites) {
final StringBuilder buf = new StringBuilder();
for (String c: javaCipherSuites) {
if (c == null) {
break;
}
String converted = toOpenSsl(c);
if (converted != null) {
c = converted;
}
buf.append(c);
buf.append(':');
}
if (buf.length() > 0) {
buf.setLength(buf.length() - 1);
return buf.toString();
} else {
return "";
}
}
/** /**
* Converts the specified Java cipher suite to its corresponding OpenSSL cipher suite name. * Converts the specified Java cipher suite to its corresponding OpenSSL cipher suite name.
* *
@ -423,5 +396,47 @@ final class CipherSuiteConverter {
return hmacAlgo; return hmacAlgo;
} }
/**
* Convert the given ciphers if needed to OpenSSL format and append them to the correct {@link StringBuilder}
* depending on if its a TLSv1.3 cipher or not. If this methods returns without throwing an exception its
* guaranteed that at least one of the {@link StringBuilder}s contain some ciphers that can be used to configure
* OpenSSL.
*/
static void convertToCipherStrings(
Iterable<String> cipherSuites, StringBuilder cipherBuilder, StringBuilder cipherTLSv13Builder) {
for (String c: cipherSuites) {
if (c == null) {
break;
}
String converted = toOpenSsl(c);
if (converted == null) {
converted = c;
}
if (!OpenSsl.isCipherSuiteAvailable(converted)) {
throw new IllegalArgumentException("unsupported cipher suite: " + c + '(' + converted + ')');
}
if (SslUtils.isTLSv13Cipher(converted)) {
cipherTLSv13Builder.append(converted);
cipherTLSv13Builder.append(':');
} else {
cipherBuilder.append(converted);
cipherBuilder.append(':');
}
}
if (cipherBuilder.length() == 0 && cipherTLSv13Builder.length() == 0) {
throw new IllegalArgumentException("empty cipher suites");
}
if (cipherBuilder.length() > 0) {
cipherBuilder.setLength(cipherBuilder.length() - 1);
}
if (cipherTLSv13Builder.length() > 0) {
cipherTLSv13Builder.setLength(cipherTLSv13Builder.length() - 1);
}
}
private CipherSuiteConverter() { } private CipherSuiteConverter() { }
} }

View File

@ -17,6 +17,7 @@
package io.netty.handler.ssl; package io.netty.handler.ssl;
import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.ByteBufAllocator;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
@ -26,6 +27,7 @@ import java.security.InvalidAlgorithmParameterException;
import java.security.KeyException; import java.security.KeyException;
import java.security.KeyStoreException; import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.Security; import java.security.Security;
import java.security.UnrecoverableKeyException; import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
@ -34,6 +36,7 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -58,11 +61,13 @@ public class JdkSslContext extends SslContext {
static final String PROTOCOL = "TLS"; static final String PROTOCOL = "TLS";
private static final String[] DEFAULT_PROTOCOLS; private static final String[] DEFAULT_PROTOCOLS;
private static final List<String> DEFAULT_CIPHERS; private static final List<String> DEFAULT_CIPHERS;
private static final List<String> DEFAULT_CIPHERS_NON_TLSV13;
private static final Set<String> SUPPORTED_CIPHERS; private static final Set<String> SUPPORTED_CIPHERS;
private static final Set<String> SUPPORTED_CIPHERS_NON_TLSV13;
private static final Provider DEFAULT_PROVIDER;
static { static {
SSLContext context; SSLContext context;
int i;
try { try {
context = SSLContext.getInstance(PROTOCOL); context = SSLContext.getInstance(PROTOCOL);
context.init(null, null, null); context.init(null, null, null);
@ -70,31 +75,54 @@ public class JdkSslContext extends SslContext {
throw new Error("failed to initialize the default SSL context", e); throw new Error("failed to initialize the default SSL context", e);
} }
SSLEngine engine = context.createSSLEngine(); DEFAULT_PROVIDER = context.getProvider();
SSLEngine engine = context.createSSLEngine();
DEFAULT_PROTOCOLS = defaultProtocols(engine);
SUPPORTED_CIPHERS = Collections.unmodifiableSet(supportedCiphers(engine));
DEFAULT_CIPHERS = Collections.unmodifiableList(defaultCiphers(engine, SUPPORTED_CIPHERS));
List<String> ciphersNonTLSv13 = new ArrayList<String>(DEFAULT_CIPHERS);
ciphersNonTLSv13.removeAll(Arrays.asList(SslUtils.DEFAULT_TLSV13_CIPHER_SUITES));
DEFAULT_CIPHERS_NON_TLSV13 = Collections.unmodifiableList(ciphersNonTLSv13);
Set<String> suppertedCiphersNonTLSv13 = new LinkedHashSet<String>(SUPPORTED_CIPHERS);
suppertedCiphersNonTLSv13.removeAll(Arrays.asList(SslUtils.DEFAULT_TLSV13_CIPHER_SUITES));
SUPPORTED_CIPHERS_NON_TLSV13 = Collections.unmodifiableSet(suppertedCiphersNonTLSv13);
if (logger.isDebugEnabled()) {
logger.debug("Default protocols (JDK): {} ", Arrays.asList(DEFAULT_PROTOCOLS));
logger.debug("Default cipher suites (JDK): {}", DEFAULT_CIPHERS);
}
}
private static String[] defaultProtocols(SSLEngine engine) {
// Choose the sensible default list of protocols. // Choose the sensible default list of protocols.
final String[] supportedProtocols = engine.getSupportedProtocols(); final String[] supportedProtocols = engine.getSupportedProtocols();
Set<String> supportedProtocolsSet = new HashSet<String>(supportedProtocols.length); Set<String> supportedProtocolsSet = new HashSet<String>(supportedProtocols.length);
for (i = 0; i < supportedProtocols.length; ++i) { for (int i = 0; i < supportedProtocols.length; ++i) {
supportedProtocolsSet.add(supportedProtocols[i]); supportedProtocolsSet.add(supportedProtocols[i]);
} }
List<String> protocols = new ArrayList<String>(); List<String> protocols = new ArrayList<String>();
addIfSupported( addIfSupported(
supportedProtocolsSet, protocols, supportedProtocolsSet, protocols,
"TLSv1.2", "TLSv1.1", "TLSv1"); // Do not include TLSv1.3 for now by default.
SslUtils.PROTOCOL_TLS_V1_2, SslUtils.PROTOCOL_TLS_V1_1, SslUtils.PROTOCOL_TLS_V1);
if (!protocols.isEmpty()) { if (!protocols.isEmpty()) {
DEFAULT_PROTOCOLS = protocols.toArray(new String[0]); return protocols.toArray(new String[0]);
} else {
DEFAULT_PROTOCOLS = engine.getEnabledProtocols();
} }
return engine.getEnabledProtocols();
}
private static Set<String> supportedCiphers(SSLEngine engine) {
// Choose the sensible default list of cipher suites. // Choose the sensible default list of cipher suites.
final String[] supportedCiphers = engine.getSupportedCipherSuites(); final String[] supportedCiphers = engine.getSupportedCipherSuites();
SUPPORTED_CIPHERS = new HashSet<String>(supportedCiphers.length); Set<String> supportedCiphersSet = new LinkedHashSet<String>(supportedCiphers.length);
for (i = 0; i < supportedCiphers.length; ++i) { for (int i = 0; i < supportedCiphers.length; ++i) {
String supportedCipher = supportedCiphers[i]; String supportedCipher = supportedCiphers[i];
SUPPORTED_CIPHERS.add(supportedCipher); supportedCiphersSet.add(supportedCipher);
// IBM's J9 JVM utilizes a custom naming scheme for ciphers and only returns ciphers with the "SSL_" // IBM's J9 JVM utilizes a custom naming scheme for ciphers and only returns ciphers with the "SSL_"
// prefix instead of the "TLS_" prefix (as defined in the JSSE cipher suite names [1]). According to IBM's // prefix instead of the "TLS_" prefix (as defined in the JSSE cipher suite names [1]). According to IBM's
// documentation [2] the "SSL_" prefix is "interchangeable" with the "TLS_" prefix. // documentation [2] the "SSL_" prefix is "interchangeable" with the "TLS_" prefix.
@ -108,21 +136,29 @@ public class JdkSslContext extends SslContext {
final String tlsPrefixedCipherName = "TLS_" + supportedCipher.substring("SSL_".length()); final String tlsPrefixedCipherName = "TLS_" + supportedCipher.substring("SSL_".length());
try { try {
engine.setEnabledCipherSuites(new String[]{tlsPrefixedCipherName}); engine.setEnabledCipherSuites(new String[]{tlsPrefixedCipherName});
SUPPORTED_CIPHERS.add(tlsPrefixedCipherName); supportedCiphersSet.add(tlsPrefixedCipherName);
} catch (IllegalArgumentException ignored) { } catch (IllegalArgumentException ignored) {
// The cipher is not supported ... move on to the next cipher. // The cipher is not supported ... move on to the next cipher.
} }
} }
} }
List<String> ciphers = new ArrayList<String>(); return supportedCiphersSet;
addIfSupported(SUPPORTED_CIPHERS, ciphers, DEFAULT_CIPHER_SUITES); }
useFallbackCiphersIfDefaultIsEmpty(ciphers, engine.getEnabledCipherSuites());
DEFAULT_CIPHERS = Collections.unmodifiableList(ciphers);
if (logger.isDebugEnabled()) { private static List<String> defaultCiphers(SSLEngine engine, Set<String> supportedCiphers) {
logger.debug("Default protocols (JDK): {} ", Arrays.asList(DEFAULT_PROTOCOLS)); List<String> ciphers = new ArrayList<String>();
logger.debug("Default cipher suites (JDK): {}", DEFAULT_CIPHERS); addIfSupported(supportedCiphers, ciphers, DEFAULT_CIPHER_SUITES);
useFallbackCiphersIfDefaultIsEmpty(ciphers, engine.getEnabledCipherSuites());
return ciphers;
}
private static boolean isTlsV13Supported(String[] protocols) {
for (String protocol: protocols) {
if (SslUtils.PROTOCOL_TLS_V1_3.equals(protocol)) {
return true;
}
} }
return false;
} }
private final String[] protocols; private final String[] protocols;
@ -205,11 +241,49 @@ public class JdkSslContext extends SslContext {
super(startTls); super(startTls);
this.apn = checkNotNull(apn, "apn"); this.apn = checkNotNull(apn, "apn");
this.clientAuth = checkNotNull(clientAuth, "clientAuth"); 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.sslContext = checkNotNull(sslContext, "sslContext");
final List<String> defaultCiphers;
final Set<String> supportedCiphers;
if (DEFAULT_PROVIDER.equals(sslContext.getProvider())) {
this.protocols = protocols == null? DEFAULT_PROTOCOLS : protocols;
if (isTlsV13Supported(this.protocols)) {
supportedCiphers = SUPPORTED_CIPHERS;
defaultCiphers = DEFAULT_CIPHERS;
} else {
// TLSv1.3 is not supported, ensure we do not include any TLSv1.3 ciphersuite.
supportedCiphers = SUPPORTED_CIPHERS_NON_TLSV13;
defaultCiphers = DEFAULT_CIPHERS_NON_TLSV13;
}
} else {
// This is a different Provider then the one used by the JDK by default so we can not just assume
// the same protocols and ciphers are supported. For example even if Java11+ is used Conscrypt will
// not support TLSv1.3 and the TLSv1.3 ciphersuites.
SSLEngine engine = sslContext.createSSLEngine();
try {
if (protocols == null) {
this.protocols = defaultProtocols(engine);
} else {
this.protocols = protocols;
}
supportedCiphers = supportedCiphers(engine);
defaultCiphers = defaultCiphers(engine, supportedCiphers);
if (!isTlsV13Supported(this.protocols)) {
// TLSv1.3 is not supported, ensure we do not include any TLSv1.3 ciphersuite.
for (String cipher: SslUtils.DEFAULT_TLSV13_CIPHER_SUITES) {
supportedCiphers.remove(cipher);
defaultCiphers.remove(cipher);
}
}
} finally {
ReferenceCountUtil.release(engine);
}
}
cipherSuites = checkNotNull(cipherFilter, "cipherFilter").filterCipherSuites(
ciphers, defaultCiphers, supportedCiphers);
unmodifiableCipherSuites = Collections.unmodifiableList(Arrays.asList(cipherSuites));
this.isClient = isClient; this.isClient = isClient;
} }

View File

@ -31,6 +31,7 @@ import io.netty.util.internal.SystemPropertyUtil;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
import javax.net.ssl.SSLException;
import java.security.AccessController; import java.security.AccessController;
import java.security.PrivilegedAction; import java.security.PrivilegedAction;
import java.util.ArrayList; import java.util.ArrayList;
@ -48,6 +49,7 @@ import static io.netty.handler.ssl.SslUtils.PROTOCOL_SSL_V3;
import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1; import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1;
import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_1; import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_1;
import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_2; import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_2;
import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_3;
/** /**
* Tells if <a href="http://netty.io/wiki/forked-tomcat-native.html">{@code netty-tcnative}</a> and its OpenSSL support * Tells if <a href="http://netty.io/wiki/forked-tomcat-native.html">{@code netty-tcnative}</a> and its OpenSSL support
@ -66,6 +68,12 @@ public final class OpenSsl {
private static final boolean SUPPORTS_HOSTNAME_VALIDATION; private static final boolean SUPPORTS_HOSTNAME_VALIDATION;
private static final boolean USE_KEYMANAGER_FACTORY; private static final boolean USE_KEYMANAGER_FACTORY;
private static final boolean SUPPORTS_OCSP; private static final boolean SUPPORTS_OCSP;
private static final String TLSV13_CIPHERS = "TLS_AES_256_GCM_SHA384" + ':' +
"TLS_CHACHA20_POLY1305_SHA256" + ':' +
"TLS_AES_128_GCM_SHA256" + ':' +
"TLS_AES_128_CCM_8_SHA256" + ':' +
"TLS_AES_128_CCM_SHA256";
private static final boolean TLSV13_SUPPORTED;
static final Set<String> SUPPORTED_PROTOCOLS_SET; static final Set<String> SUPPORTED_PROTOCOLS_SET;
@ -139,17 +147,30 @@ public final class OpenSsl {
boolean supportsKeyManagerFactory = false; boolean supportsKeyManagerFactory = false;
boolean useKeyManagerFactory = false; boolean useKeyManagerFactory = false;
boolean supportsHostNameValidation = false; boolean supportsHostNameValidation = false;
boolean tlsv13Supported = false;
try { try {
final long sslCtx = SSLContext.make(SSL.SSL_PROTOCOL_ALL, SSL.SSL_MODE_SERVER); final long sslCtx = SSLContext.make(SSL.SSL_PROTOCOL_ALL, SSL.SSL_MODE_SERVER);
long certBio = 0; long certBio = 0;
SelfSignedCertificate cert = null; SelfSignedCertificate cert = null;
try { try {
SSLContext.setCipherSuite(sslCtx, "ALL"); if (PlatformDependent.javaVersion() >= 11) {
try {
SSLContext.setCipherSuite(sslCtx, TLSV13_CIPHERS, true);
tlsv13Supported = true;
} catch (Exception ignore) {
tlsv13Supported = false;
}
}
SSLContext.setCipherSuite(sslCtx, "ALL", false);
final long ssl = SSL.newSSL(sslCtx, true); final long ssl = SSL.newSSL(sslCtx, true);
try { try {
for (String c: SSL.getCiphers(ssl)) { for (String c: SSL.getCiphers(ssl)) {
// Filter out bad input. // Filter out bad input.
if (c == null || c.isEmpty() || availableOpenSslCipherSuites.contains(c)) { if (c == null || c.isEmpty() || availableOpenSslCipherSuites.contains(c) ||
// Filter out TLSv1.3 ciphers if not supported.
!tlsv13Supported && SslUtils.isTLSv13Cipher(c)) {
continue; continue;
} }
availableOpenSslCipherSuites.add(c); availableOpenSslCipherSuites.add(c);
@ -200,8 +221,13 @@ public final class OpenSsl {
AVAILABLE_OPENSSL_CIPHER_SUITES.size() * 2); AVAILABLE_OPENSSL_CIPHER_SUITES.size() * 2);
for (String cipher: AVAILABLE_OPENSSL_CIPHER_SUITES) { for (String cipher: AVAILABLE_OPENSSL_CIPHER_SUITES) {
// Included converted but also openssl cipher name // Included converted but also openssl cipher name
availableJavaCipherSuites.add(CipherSuiteConverter.toJava(cipher, "TLS")); if (!SslUtils.isTLSv13Cipher(cipher)) {
availableJavaCipherSuites.add(CipherSuiteConverter.toJava(cipher, "SSL")); availableJavaCipherSuites.add(CipherSuiteConverter.toJava(cipher, "TLS"));
availableJavaCipherSuites.add(CipherSuiteConverter.toJava(cipher, "SSL"));
} else {
// TLSv1.3 ciphers have the correct format.
availableJavaCipherSuites.add(cipher);
}
} }
addIfSupported(availableJavaCipherSuites, defaultCiphers, DEFAULT_CIPHER_SUITES); addIfSupported(availableJavaCipherSuites, defaultCiphers, DEFAULT_CIPHER_SUITES);
@ -239,6 +265,18 @@ public final class OpenSsl {
protocols.add(PROTOCOL_TLS_V1_2); protocols.add(PROTOCOL_TLS_V1_2);
} }
// This is only supported by java11 and later.
if (tlsv13Supported && doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1_3, SSL.SSL_OP_NO_TLSv1_3)
&& PlatformDependent.javaVersion() >= 11) {
// We can only support TLS1.3 when using Java 11 or higher as otherwise it will fail to create the
// internal instance of an sun.security.ssl.ProtocolVersion as can not parse the version string :/
// See http://mail.openjdk.java.net/pipermail/security-dev/2018-September/018242.html
protocols.add(PROTOCOL_TLS_V1_3);
TLSV13_SUPPORTED = true;
} else {
TLSV13_SUPPORTED = false;
}
SUPPORTED_PROTOCOLS_SET = Collections.unmodifiableSet(protocols); SUPPORTED_PROTOCOLS_SET = Collections.unmodifiableSet(protocols);
SUPPORTS_OCSP = doesSupportOcsp(); SUPPORTS_OCSP = doesSupportOcsp();
@ -256,6 +294,7 @@ public final class OpenSsl {
USE_KEYMANAGER_FACTORY = false; USE_KEYMANAGER_FACTORY = false;
SUPPORTED_PROTOCOLS_SET = Collections.emptySet(); SUPPORTED_PROTOCOLS_SET = Collections.emptySet();
SUPPORTS_OCSP = false; SUPPORTS_OCSP = false;
TLSV13_SUPPORTED = false;
} }
} }
@ -450,4 +489,8 @@ public final class OpenSsl {
ReferenceCountUtil.safeRelease(counted); ReferenceCountUtil.safeRelease(counted);
} }
} }
static boolean isTlsv13Supported() {
return TLSV13_SUPPORTED;
}
} }

View File

@ -24,8 +24,11 @@ import io.netty.internal.tcnative.SSLContext;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set; import java.util.Set;
import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.KeyManagerFactory;
@ -47,6 +50,12 @@ import javax.security.auth.x500.X500Principal;
public final class ReferenceCountedOpenSslClientContext extends ReferenceCountedOpenSslContext { public final class ReferenceCountedOpenSslClientContext extends ReferenceCountedOpenSslContext {
private static final InternalLogger logger = private static final InternalLogger logger =
InternalLoggerFactory.getInstance(ReferenceCountedOpenSslClientContext.class); InternalLoggerFactory.getInstance(ReferenceCountedOpenSslClientContext.class);
private static final Set<String> SUPPORTED_KEY_TYPES = Collections.unmodifiableSet(new LinkedHashSet<String>(
Arrays.asList(OpenSslKeyMaterialManager.KEY_TYPE_RSA,
OpenSslKeyMaterialManager.KEY_TYPE_DH_RSA,
OpenSslKeyMaterialManager.KEY_TYPE_EC,
OpenSslKeyMaterialManager.KEY_TYPE_EC_RSA,
OpenSslKeyMaterialManager.KEY_TYPE_EC_EC)));
private final OpenSslSessionContext sessionContext; private final OpenSslSessionContext sessionContext;
ReferenceCountedOpenSslClientContext(X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory, ReferenceCountedOpenSslClientContext(X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory,
@ -277,7 +286,8 @@ public final class ReferenceCountedOpenSslClientContext extends ReferenceCounted
*/ */
private static Set<String> supportedClientKeyTypes(byte[] clientCertificateTypes) { private static Set<String> supportedClientKeyTypes(byte[] clientCertificateTypes) {
if (clientCertificateTypes == null) { if (clientCertificateTypes == null) {
return Collections.emptySet(); // Try all of the supported key types.
return SUPPORTED_KEY_TYPES;
} }
Set<String> result = new HashSet<String>(clientCertificateTypes.length); Set<String> result = new HashSet<String>(clientCertificateTypes.length);
for (byte keyTypeCode : clientCertificateTypes) { for (byte keyTypeCode : clientCertificateTypes) {

View File

@ -225,26 +225,71 @@ public abstract class ReferenceCountedOpenSslContext extends SslContext implemen
boolean success = false; boolean success = false;
try { try {
try { try {
int opts = SSL.SSL_PROTOCOL_SSLV3 | SSL.SSL_PROTOCOL_TLSV1 | int protocolOpts = SSL.SSL_PROTOCOL_SSLV3 | SSL.SSL_PROTOCOL_TLSV1 |
SSL.SSL_PROTOCOL_TLSV1_1 | SSL.SSL_PROTOCOL_TLSV1_2; SSL.SSL_PROTOCOL_TLSV1_1 | SSL.SSL_PROTOCOL_TLSV1_2;
ctx = SSLContext.make(opts, mode); if (OpenSsl.isTlsv13Supported()) {
protocolOpts |= SSL.SSL_PROTOCOL_TLSV1_3;
}
ctx = SSLContext.make(protocolOpts, mode);
} catch (Exception e) { } catch (Exception e) {
throw new SSLException("failed to create an SSL_CTX", e); throw new SSLException("failed to create an SSL_CTX", e);
} }
SSLContext.setOptions(ctx, SSLContext.getOptions(ctx) | boolean tlsv13Supported = OpenSsl.isTlsv13Supported();
SSL.SSL_OP_NO_SSLv2 | StringBuilder cipherBuilder = new StringBuilder();
SSL.SSL_OP_NO_SSLv3 | StringBuilder cipherTLSv13Builder = new StringBuilder();
SSL.SSL_OP_CIPHER_SERVER_PREFERENCE |
// We do not support compression at the moment so we should explicitly disable it. /* List the ciphers that are permitted to negotiate. */
SSL.SSL_OP_NO_COMPRESSION | try {
if (unmodifiableCiphers.isEmpty()) {
// Set non TLSv1.3 ciphers.
SSLContext.setCipherSuite(ctx, StringUtil.EMPTY_STRING, false);
if (tlsv13Supported) {
// Set TLSv1.3 ciphers.
SSLContext.setCipherSuite(ctx, StringUtil.EMPTY_STRING, true);
}
} else {
CipherSuiteConverter.convertToCipherStrings(
unmodifiableCiphers, cipherBuilder, cipherTLSv13Builder);
// Disable ticket support by default to be more inline with SSLEngineImpl of the JDK. // Set non TLSv1.3 ciphers.
// This also let SSLSession.getId() work the same way for the JDK implementation and the SSLContext.setCipherSuite(ctx, cipherBuilder.toString(), false);
// OpenSSLEngine. If tickets are supported SSLSession.getId() will only return an ID on the if (tlsv13Supported) {
// server-side if it could make use of tickets. // Set TLSv1.3 ciphers.
SSL.SSL_OP_NO_TICKET); SSLContext.setCipherSuite(ctx, cipherTLSv13Builder.toString(), true);
}
}
} catch (SSLException e) {
throw e;
} catch (Exception e) {
throw new SSLException("failed to set cipher suite: " + unmodifiableCiphers, e);
}
int options = SSLContext.getOptions(ctx) |
SSL.SSL_OP_NO_SSLv2 |
SSL.SSL_OP_NO_SSLv3 |
// Disable TLSv1.3 by default for now. Even if TLSv1.3 is not supported this will
// work fine as in this case SSL_OP_NO_TLSv1_3 will be 0.
SSL.SSL_OP_NO_TLSv1_3 |
SSL.SSL_OP_CIPHER_SERVER_PREFERENCE |
// We do not support compression at the moment so we should explicitly disable it.
SSL.SSL_OP_NO_COMPRESSION |
// Disable ticket support by default to be more inline with SSLEngineImpl of the JDK.
// This also let SSLSession.getId() work the same way for the JDK implementation and the
// OpenSSLEngine. If tickets are supported SSLSession.getId() will only return an ID on the
// server-side if it could make use of tickets.
SSL.SSL_OP_NO_TICKET;
if (cipherBuilder.length() == 0) {
// No ciphers that are compatible with SSLv2 / SSLv3 / TLSv1 / TLSv1.1 / TLSv1.2
options |= SSL.SSL_OP_NO_SSLv2 | SSL.SSL_OP_NO_SSLv3 | SSL.SSL_OP_NO_TLSv1
| SSL.SSL_OP_NO_TLSv1_1 | SSL.SSL_OP_NO_TLSv1_2;
}
SSLContext.setOptions(ctx, options);
// We need to enable SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER as the memory address may change between // We need to enable SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER as the memory address may change between
// calling OpenSSLEngine.wrap(...). // calling OpenSSLEngine.wrap(...).
@ -255,15 +300,6 @@ public abstract class ReferenceCountedOpenSslContext extends SslContext implemen
SSLContext.setTmpDHLength(ctx, DH_KEY_LENGTH); SSLContext.setTmpDHLength(ctx, DH_KEY_LENGTH);
} }
/* List the ciphers that are permitted to negotiate. */
try {
SSLContext.setCipherSuite(ctx, CipherSuiteConverter.toOpenSsl(unmodifiableCiphers));
} catch (SSLException e) {
throw e;
} catch (Exception e) {
throw new SSLException("failed to set cipher suite: " + unmodifiableCiphers, e);
}
List<String> nextProtoList = apn.protocols(); List<String> nextProtoList = apn.protocols();
/* Set next protocols for next protocol negotiation extension, if specified */ /* Set next protocols for next protocol negotiation extension, if specified */
if (!nextProtoList.isEmpty()) { if (!nextProtoList.isEmpty()) {

View File

@ -66,9 +66,8 @@ import static io.netty.handler.ssl.SslUtils.PROTOCOL_SSL_V3;
import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1; import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1;
import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_1; import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_1;
import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_2; import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_2;
import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_3;
import static io.netty.handler.ssl.SslUtils.SSL_RECORD_HEADER_LENGTH; import static io.netty.handler.ssl.SslUtils.SSL_RECORD_HEADER_LENGTH;
import static io.netty.internal.tcnative.SSL.SSL_MAX_PLAINTEXT_LENGTH;
import static io.netty.internal.tcnative.SSL.SSL_MAX_RECORD_LENGTH;
import static io.netty.util.internal.EmptyArrays.EMPTY_CERTIFICATES; import static io.netty.util.internal.EmptyArrays.EMPTY_CERTIFICATES;
import static io.netty.util.internal.EmptyArrays.EMPTY_JAVAX_X509_CERTIFICATES; import static io.netty.util.internal.EmptyArrays.EMPTY_JAVAX_X509_CERTIFICATES;
import static io.netty.util.internal.ObjectUtil.checkNotNull; import static io.netty.util.internal.ObjectUtil.checkNotNull;
@ -109,12 +108,14 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
private static final int OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1 = 2; private static final int OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1 = 2;
private static final int OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_1 = 3; private static final int OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_1 = 3;
private static final int OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_2 = 4; private static final int OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_2 = 4;
private static final int OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_3 = 5;
private static final int[] OPENSSL_OP_NO_PROTOCOLS = { private static final int[] OPENSSL_OP_NO_PROTOCOLS = {
SSL.SSL_OP_NO_SSLv2, SSL.SSL_OP_NO_SSLv2,
SSL.SSL_OP_NO_SSLv3, SSL.SSL_OP_NO_SSLv3,
SSL.SSL_OP_NO_TLSv1, SSL.SSL_OP_NO_TLSv1,
SSL.SSL_OP_NO_TLSv1_1, SSL.SSL_OP_NO_TLSv1_1,
SSL.SSL_OP_NO_TLSv1_2 SSL.SSL_OP_NO_TLSv1_2,
SSL.SSL_OP_NO_TLSv1_3
}; };
/** /**
* <a href="https://www.openssl.org/docs/man1.0.2/crypto/X509_check_host.html">The flags argument is usually 0</a>. * <a href="https://www.openssl.org/docs/man1.0.2/crypto/X509_check_host.html">The flags argument is usually 0</a>.
@ -124,11 +125,11 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
/** /**
* Depends upon tcnative ... only use if tcnative is available! * Depends upon tcnative ... only use if tcnative is available!
*/ */
static final int MAX_PLAINTEXT_LENGTH = SSL_MAX_PLAINTEXT_LENGTH; static final int MAX_PLAINTEXT_LENGTH = SSL.SSL_MAX_PLAINTEXT_LENGTH;
/** /**
* Depends upon tcnative ... only use if tcnative is available! * Depends upon tcnative ... only use if tcnative is available!
*/ */
private static final int MAX_RECORD_SIZE = SSL_MAX_RECORD_LENGTH; private static final int MAX_RECORD_SIZE = SSL.SSL_MAX_RECORD_LENGTH;
private static final AtomicIntegerFieldUpdater<ReferenceCountedOpenSslEngine> DESTROYED_UPDATER = private static final AtomicIntegerFieldUpdater<ReferenceCountedOpenSslEngine> DESTROYED_UPDATER =
AtomicIntegerFieldUpdater.newUpdater(ReferenceCountedOpenSslEngine.class, "destroyed"); AtomicIntegerFieldUpdater.newUpdater(ReferenceCountedOpenSslEngine.class, "destroyed");
@ -1206,7 +1207,10 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
// As rejectRemoteInitiatedRenegotiation() is called in a finally block we also need to check if we shutdown // As rejectRemoteInitiatedRenegotiation() is called in a finally block we also need to check if we shutdown
// the engine before as otherwise SSL.getHandshakeCount(ssl) will throw an NPE if the passed in ssl is 0. // the engine before as otherwise SSL.getHandshakeCount(ssl) will throw an NPE if the passed in ssl is 0.
// See https://github.com/netty/netty/issues/7353 // See https://github.com/netty/netty/issues/7353
if (!isDestroyed() && SSL.getHandshakeCount(ssl) > 1) { if (!isDestroyed() && SSL.getHandshakeCount(ssl) > 1 &&
// As we may count multiple handshakes when TLSv1.3 is used we should just ignore this here as
// renegotiation is not supported in TLSv1.3 as per spec.
!SslUtils.PROTOCOL_TLS_V1_3.equals(session.getProtocol()) && handshakeState == HandshakeState.FINISHED) {
// TODO: In future versions me may also want to send a fatal_alert to the client and so notify it // TODO: In future versions me may also want to send a fatal_alert to the client and so notify it
// that the renegotiation failed. // that the renegotiation failed.
shutdown(); shutdown();
@ -1379,15 +1383,18 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
if (enabled == null) { if (enabled == null) {
return EmptyArrays.EMPTY_STRINGS; return EmptyArrays.EMPTY_STRINGS;
} else { } else {
List<String> enabledList = new ArrayList<String>();
synchronized (this) { synchronized (this) {
for (int i = 0; i < enabled.length; i++) { for (int i = 0; i < enabled.length; i++) {
String mapped = toJavaCipherSuite(enabled[i]); String mapped = toJavaCipherSuite(enabled[i]);
if (mapped != null) { final String cipher = mapped == null ? enabled[i] : mapped;
enabled[i] = mapped; if (!OpenSsl.isTlsv13Supported() && SslUtils.isTLSv13Cipher(cipher)) {
continue;
} }
enabledList.add(cipher);
} }
} }
return enabled; return enabledList.toArray(new String[0]);
} }
} }
@ -1396,35 +1403,28 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
checkNotNull(cipherSuites, "cipherSuites"); checkNotNull(cipherSuites, "cipherSuites");
final StringBuilder buf = new StringBuilder(); final StringBuilder buf = new StringBuilder();
for (String c: cipherSuites) { final StringBuilder bufTLSv13 = new StringBuilder();
if (c == null) {
break;
}
String converted = CipherSuiteConverter.toOpenSsl(c);
if (converted == null) {
converted = c;
}
if (!OpenSsl.isCipherSuiteAvailable(converted)) {
throw new IllegalArgumentException("unsupported cipher suite: " + c + '(' + converted + ')');
}
buf.append(converted);
buf.append(':');
}
if (buf.length() == 0) {
throw new IllegalArgumentException("empty cipher suites");
}
buf.setLength(buf.length() - 1);
CipherSuiteConverter.convertToCipherStrings(Arrays.asList(cipherSuites), buf, bufTLSv13);
final String cipherSuiteSpec = buf.toString(); final String cipherSuiteSpec = buf.toString();
final String cipherSuiteSpecTLSv13 = bufTLSv13.toString();
if (!OpenSsl.isTlsv13Supported() && !cipherSuiteSpecTLSv13.isEmpty()) {
throw new IllegalArgumentException("TLSv1.3 is not supported by this java version.");
}
synchronized (this) { synchronized (this) {
if (!isDestroyed()) { if (!isDestroyed()) {
// TODO: Should we also adjust the protocols based on if there are any ciphers left that can be used
// for TLSv1.3 or for previor SSL/TLS versions ?
try { try {
SSL.setCipherSuites(ssl, cipherSuiteSpec); // Set non TLSv1.3 ciphers.
SSL.setCipherSuites(ssl, cipherSuiteSpec, false);
if (OpenSsl.isTlsv13Supported()) {
// Set TLSv1.3 ciphers.
SSL.setCipherSuites(ssl, cipherSuiteSpecTLSv13, true);
}
} catch (Exception e) { } catch (Exception e) {
throw new IllegalStateException("failed to enable cipher suites: " + cipherSuiteSpec, e); throw new IllegalStateException("failed to enable cipher suites: " + cipherSuiteSpec, e);
} }
@ -1462,6 +1462,9 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
if (isProtocolEnabled(opts, SSL.SSL_OP_NO_TLSv1_2, PROTOCOL_TLS_V1_2)) { if (isProtocolEnabled(opts, SSL.SSL_OP_NO_TLSv1_2, PROTOCOL_TLS_V1_2)) {
enabled.add(PROTOCOL_TLS_V1_2); enabled.add(PROTOCOL_TLS_V1_2);
} }
if (isProtocolEnabled(opts, SSL.SSL_OP_NO_TLSv1_3, PROTOCOL_TLS_V1_3)) {
enabled.add(PROTOCOL_TLS_V1_3);
}
if (isProtocolEnabled(opts, SSL.SSL_OP_NO_SSLv2, PROTOCOL_SSL_V2)) { if (isProtocolEnabled(opts, SSL.SSL_OP_NO_SSLv2, PROTOCOL_SSL_V2)) {
enabled.add(PROTOCOL_SSL_V2); enabled.add(PROTOCOL_SSL_V2);
} }
@ -1533,13 +1536,20 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
if (maxProtocolIndex < OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_2) { if (maxProtocolIndex < OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_2) {
maxProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_2; maxProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_2;
} }
} else if (p.equals(PROTOCOL_TLS_V1_3)) {
if (minProtocolIndex > OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_3) {
minProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_3;
}
if (maxProtocolIndex < OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_3) {
maxProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_3;
}
} }
} }
synchronized (this) { synchronized (this) {
if (!isDestroyed()) { if (!isDestroyed()) {
// Clear out options which disable protocols // Clear out options which disable protocols
SSL.clearOptions(ssl, SSL.SSL_OP_NO_SSLv2 | SSL.SSL_OP_NO_SSLv3 | SSL.SSL_OP_NO_TLSv1 | SSL.clearOptions(ssl, SSL.SSL_OP_NO_SSLv2 | SSL.SSL_OP_NO_SSLv3 | SSL.SSL_OP_NO_TLSv1 |
SSL.SSL_OP_NO_TLSv1_1 | SSL.SSL_OP_NO_TLSv1_2); SSL.SSL_OP_NO_TLSv1_1 | SSL.SSL_OP_NO_TLSv1_2 | SSL.SSL_OP_NO_TLSv1_3);
int opts = 0; int opts = 0;
for (int i = 0; i < minProtocolIndex; ++i) { for (int i = 0; i < minProtocolIndex; ++i) {

View File

@ -22,9 +22,14 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.base64.Base64; import io.netty.handler.codec.base64.Base64;
import io.netty.handler.codec.base64.Base64Dialect; import io.netty.handler.codec.base64.Base64Dialect;
import io.netty.util.NetUtil; import io.netty.util.NetUtil;
import io.netty.util.internal.EmptyArrays;
import io.netty.util.internal.PlatformDependent;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -36,7 +41,11 @@ import static java.util.Arrays.asList;
* Constants for SSL packets. * Constants for SSL packets.
*/ */
final class SslUtils { final class SslUtils {
// See https://tools.ietf.org/html/rfc8446#appendix-B.4
private static final Set<String> TLSV13_CIPHERS = Collections.unmodifiableSet(new HashSet<String>(
asList("TLS_AES_256_GCM_SHA384", "TLS_CHACHA20_POLY1305_SHA256",
"TLS_AES_128_GCM_SHA256", "TLS_AES_128_CCM_8_SHA256",
"TLS_AES_128_CCM_SHA256")));
// Protocols // Protocols
static final String PROTOCOL_SSL_V2_HELLO = "SSLv2Hello"; static final String PROTOCOL_SSL_V2_HELLO = "SSLv2Hello";
static final String PROTOCOL_SSL_V2 = "SSLv2"; static final String PROTOCOL_SSL_V2 = "SSLv2";
@ -44,6 +53,7 @@ final class SslUtils {
static final String PROTOCOL_TLS_V1 = "TLSv1"; static final String PROTOCOL_TLS_V1 = "TLSv1";
static final String PROTOCOL_TLS_V1_1 = "TLSv1.1"; static final String PROTOCOL_TLS_V1_1 = "TLSv1.1";
static final String PROTOCOL_TLS_V1_2 = "TLSv1.2"; static final String PROTOCOL_TLS_V1_2 = "TLSv1.2";
static final String PROTOCOL_TLS_V1_3 = "TLSv1.3";
/** /**
* change cipher spec * change cipher spec
@ -85,20 +95,36 @@ final class SslUtils {
*/ */
static final int NOT_ENCRYPTED = -2; static final int NOT_ENCRYPTED = -2;
static final String[] DEFAULT_CIPHER_SUITES = { static final String[] DEFAULT_CIPHER_SUITES;
static final String[] DEFAULT_TLSV13_CIPHER_SUITES;
static {
if (PlatformDependent.javaVersion() >= 11) {
DEFAULT_TLSV13_CIPHER_SUITES = new String[] { "TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384" };
} else {
DEFAULT_TLSV13_CIPHER_SUITES = EmptyArrays.EMPTY_STRINGS;
}
List<String> defaultCiphers = new ArrayList<String>();
// GCM (Galois/Counter Mode) requires JDK 8. // GCM (Galois/Counter Mode) requires JDK 8.
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", defaultCiphers.add("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384");
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", defaultCiphers.add("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256");
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", defaultCiphers.add("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", defaultCiphers.add("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");
// AES256 requires JCE unlimited strength jurisdiction policy files. // AES256 requires JCE unlimited strength jurisdiction policy files.
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", defaultCiphers.add("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA");
// GCM (Galois/Counter Mode) requires JDK 8. // GCM (Galois/Counter Mode) requires JDK 8.
"TLS_RSA_WITH_AES_128_GCM_SHA256", defaultCiphers.add("TLS_RSA_WITH_AES_128_GCM_SHA256");
"TLS_RSA_WITH_AES_128_CBC_SHA", defaultCiphers.add("TLS_RSA_WITH_AES_128_CBC_SHA");
// AES256 requires JCE unlimited strength jurisdiction policy files. // AES256 requires JCE unlimited strength jurisdiction policy files.
"TLS_RSA_WITH_AES_256_CBC_SHA" defaultCiphers.add("TLS_RSA_WITH_AES_256_CBC_SHA");
};
for (String tlsv13Cipher: DEFAULT_TLSV13_CIPHER_SUITES) {
defaultCiphers.add(tlsv13Cipher);
}
DEFAULT_CIPHER_SUITES = defaultCiphers.toArray(new String[0]);
}
/** /**
* Add elements from {@code names} into {@code enabled} if they are in {@code supported}. * Add elements from {@code names} into {@code enabled} if they are in {@code supported}.
@ -361,6 +387,14 @@ final class SslUtils {
!NetUtil.isValidIpV6Address(hostname); !NetUtil.isValidIpV6Address(hostname);
} }
/**
* Returns {@code true} if the the given cipher (in openssl format) is for TLSv1.3, {@code false} otherwise.
*/
static boolean isTLSv13Cipher(String cipher) {
// See https://tools.ietf.org/html/rfc8446#appendix-B.4
return TLSV13_CIPHERS.contains(cipher);
}
private SslUtils() { private SslUtils() {
} }
} }

View File

@ -125,12 +125,16 @@ public class CipherSuiteCanaryTest {
final SslContext sslServerContext = SslContextBuilder.forServer(CERT.certificate(), CERT.privateKey()) final SslContext sslServerContext = SslContextBuilder.forServer(CERT.certificate(), CERT.privateKey())
.sslProvider(serverSslProvider) .sslProvider(serverSslProvider)
.ciphers(ciphers) .ciphers(ciphers)
// As this is not a TLSv1.3 cipher we should ensure we talk something else.
.protocols(SslUtils.PROTOCOL_TLS_V1_2)
.build(); .build();
try { try {
final SslContext sslClientContext = SslContextBuilder.forClient() final SslContext sslClientContext = SslContextBuilder.forClient()
.sslProvider(clientSslProvider) .sslProvider(clientSslProvider)
.ciphers(ciphers) .ciphers(ciphers)
// As this is not a TLSv1.3 cipher we should ensure we talk something else.
.protocols(SslUtils.PROTOCOL_TLS_V1_2)
.trustManager(InsecureTrustManagerFactory.INSTANCE) .trustManager(InsecureTrustManagerFactory.INSTANCE)
.build(); .build();

View File

@ -22,8 +22,7 @@ import org.junit.Test;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.sameInstance; import static org.hamcrest.Matchers.sameInstance;
import static org.junit.Assert.assertNull; import static org.junit.Assert.*;
import static org.junit.Assert.assertThat;
public class CipherSuiteConverterTest { public class CipherSuiteConverterTest {

View File

@ -31,17 +31,17 @@ import static org.junit.Assume.assumeTrue;
@RunWith(Parameterized.class) @RunWith(Parameterized.class)
public class ConscryptJdkSslEngineInteropTest extends SSLEngineTest { public class ConscryptJdkSslEngineInteropTest extends SSLEngineTest {
@Parameterized.Parameters(name = "{index}: bufferType = {0}") @Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}")
public static Collection<Object> data() { public static Collection<Object[]> data() {
List<Object> params = new ArrayList<Object>(); List<Object[]> params = new ArrayList<Object[]>();
for (BufferType type: BufferType.values()) { for (BufferType type: BufferType.values()) {
params.add(type); params.add(new Object[] { type, ProtocolCipherCombo.tlsv12()});
} }
return params; return params;
} }
public ConscryptJdkSslEngineInteropTest(BufferType type) { public ConscryptJdkSslEngineInteropTest(BufferType type, ProtocolCipherCombo combo) {
super(type); super(type, combo);
} }
@BeforeClass @BeforeClass

View File

@ -30,17 +30,17 @@ import static org.junit.Assume.assumeTrue;
@RunWith(Parameterized.class) @RunWith(Parameterized.class)
public class ConscryptSslEngineTest extends SSLEngineTest { public class ConscryptSslEngineTest extends SSLEngineTest {
@Parameterized.Parameters(name = "{index}: bufferType = {0}") @Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}")
public static Collection<Object> data() { public static Collection<Object[]> data() {
List<Object> params = new ArrayList<Object>(); List<Object[]> params = new ArrayList<Object[]>();
for (BufferType type: BufferType.values()) { for (BufferType type: BufferType.values()) {
params.add(type); params.add(new Object[] { type, ProtocolCipherCombo.tlsv12()});
} }
return params; return params;
} }
public ConscryptSslEngineTest(BufferType type) { public ConscryptSslEngineTest(BufferType type, ProtocolCipherCombo combo) {
super(type); super(type, combo);
} }
@BeforeClass @BeforeClass

View File

@ -16,6 +16,7 @@
package io.netty.handler.ssl; package io.netty.handler.ssl;
import java.security.Provider; import java.security.Provider;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
@ -31,17 +32,17 @@ import static org.junit.Assume.assumeTrue;
@RunWith(Parameterized.class) @RunWith(Parameterized.class)
public class JdkConscryptSslEngineInteropTest extends SSLEngineTest { public class JdkConscryptSslEngineInteropTest extends SSLEngineTest {
@Parameterized.Parameters(name = "{index}: bufferType = {0}") @Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}")
public static Collection<Object> data() { public static Collection<Object[]> data() {
List<Object> params = new ArrayList<Object>(); List<Object[]> params = new ArrayList<Object[]>();
for (BufferType type: BufferType.values()) { for (BufferType type: BufferType.values()) {
params.add(type); params.add(new Object[] { type, ProtocolCipherCombo.tlsv12()});
} }
return params; return params;
} }
public JdkConscryptSslEngineInteropTest(BufferType type) { public JdkConscryptSslEngineInteropTest(BufferType type, ProtocolCipherCombo combo) {
super(type); super(type, combo);
} }
@BeforeClass @BeforeClass

View File

@ -15,6 +15,7 @@
*/ */
package io.netty.handler.ssl; package io.netty.handler.ssl;
import io.netty.util.internal.PlatformDependent;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -32,17 +33,21 @@ import static org.junit.Assume.assumeTrue;
@RunWith(Parameterized.class) @RunWith(Parameterized.class)
public class JdkOpenSslEngineInteroptTest extends SSLEngineTest { public class JdkOpenSslEngineInteroptTest extends SSLEngineTest {
@Parameterized.Parameters(name = "{index}: bufferType = {0}") @Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}")
public static Collection<Object> data() { public static Collection<Object[]> data() {
List<Object> params = new ArrayList<Object>(); List<Object[]> params = new ArrayList<Object[]>();
for (BufferType type: BufferType.values()) { for (BufferType type: BufferType.values()) {
params.add(type); params.add(new Object[] { type, ProtocolCipherCombo.tlsv12()});
if (PlatformDependent.javaVersion() >= 11 && OpenSsl.isTlsv13Supported()) {
params.add(new Object[] { type, ProtocolCipherCombo.tlsv13() });
}
} }
return params; return params;
} }
public JdkOpenSslEngineInteroptTest(BufferType type) { public JdkOpenSslEngineInteroptTest(BufferType type, ProtocolCipherCombo protocolCipherCombo) {
super(type); super(type, protocolCipherCombo);
} }
@BeforeClass @BeforeClass

View File

@ -26,6 +26,7 @@ import java.security.Provider;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import io.netty.util.internal.EmptyArrays;
import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.PlatformDependent;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
@ -141,12 +142,15 @@ public class JdkSslEngineTest extends SSLEngineTest {
private static final String FALLBACK_APPLICATION_LEVEL_PROTOCOL = "my-protocol-http1_1"; private static final String FALLBACK_APPLICATION_LEVEL_PROTOCOL = "my-protocol-http1_1";
private static final String APPLICATION_LEVEL_PROTOCOL_NOT_COMPATIBLE = "my-protocol-FOO"; private static final String APPLICATION_LEVEL_PROTOCOL_NOT_COMPATIBLE = "my-protocol-FOO";
@Parameterized.Parameters(name = "{index}: providerType = {0}, bufferType = {1}") @Parameterized.Parameters(name = "{index}: providerType = {0}, bufferType = {1}, combo = {2}")
public static Collection<Object[]> data() { public static Collection<Object[]> data() {
List<Object[]> params = new ArrayList<Object[]>(); List<Object[]> params = new ArrayList<Object[]>();
for (ProviderType providerType : ProviderType.values()) { for (ProviderType providerType : ProviderType.values()) {
for (BufferType bufferType : BufferType.values()) { for (BufferType bufferType : BufferType.values()) {
params.add(new Object[]{providerType, bufferType}); params.add(new Object[]{ providerType, bufferType, ProtocolCipherCombo.tlsv12()});
if (PlatformDependent.javaVersion() >= 11) {
params.add(new Object[] { providerType, bufferType, ProtocolCipherCombo.tlsv13() });
}
} }
} }
return params; return params;
@ -156,8 +160,8 @@ public class JdkSslEngineTest extends SSLEngineTest {
private Provider provider; private Provider provider;
public JdkSslEngineTest(ProviderType providerType, BufferType bufferType) { public JdkSslEngineTest(ProviderType providerType, BufferType bufferType, ProtocolCipherCombo protocolCipherCombo) {
super(bufferType); super(bufferType, protocolCipherCombo);
this.providerType = providerType; this.providerType = providerType;
} }
@ -235,9 +239,11 @@ public class JdkSslEngineTest extends SSLEngineTest {
InsecureTrustManagerFactory.INSTANCE, null, InsecureTrustManagerFactory.INSTANCE, null,
IdentityCipherSuiteFilter.INSTANCE, clientApn, 0, 0); IdentityCipherSuiteFilter.INSTANCE, clientApn, 0, 0);
setupHandlers(serverSslCtx, clientSslCtx); setupHandlers(new TestDelegatingSslContext(serverSslCtx), new TestDelegatingSslContext(clientSslCtx));
assertTrue(clientLatch.await(2, TimeUnit.SECONDS)); assertTrue(clientLatch.await(2, TimeUnit.SECONDS));
assertTrue(clientException instanceof SSLHandshakeException); // When using TLSv1.3 the handshake is NOT sent in an extra round trip which means there will be
// no exception reported in this case but just the channel will be closed.
assertTrue(clientException instanceof SSLHandshakeException || clientException == null);
} }
} catch (SkipTestException e) { } catch (SkipTestException e) {
// ALPN availability is dependent on the java version. If ALPN is not available because of // ALPN availability is dependent on the java version. If ALPN is not available because of
@ -358,4 +364,16 @@ public class JdkSslEngineTest extends SSLEngineTest {
super(message); super(message);
} }
} }
private final class TestDelegatingSslContext extends DelegatingSslContext {
TestDelegatingSslContext(SslContext ctx) {
super(ctx);
}
@Override
protected void initEngine(SSLEngine engine) {
engine.setEnabledProtocols(protocols());
engine.setEnabledCipherSuites(ciphers().toArray(EmptyArrays.EMPTY_STRINGS));
}
}
} }

View File

@ -67,17 +67,21 @@ public class OpenSslEngineTest extends SSLEngineTest {
private static final String PREFERRED_APPLICATION_LEVEL_PROTOCOL = "my-protocol-http2"; private static final String PREFERRED_APPLICATION_LEVEL_PROTOCOL = "my-protocol-http2";
private static final String FALLBACK_APPLICATION_LEVEL_PROTOCOL = "my-protocol-http1_1"; private static final String FALLBACK_APPLICATION_LEVEL_PROTOCOL = "my-protocol-http1_1";
@Parameterized.Parameters(name = "{index}: bufferType = {0}") @Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}")
public static Collection<Object> data() { public static Collection<Object[]> data() {
List<Object> params = new ArrayList<Object>(); List<Object[]> params = new ArrayList<Object[]>();
for (BufferType type: BufferType.values()) { for (BufferType type: BufferType.values()) {
params.add(type); params.add(new Object[] { type, ProtocolCipherCombo.tlsv12()});
if (PlatformDependent.javaVersion() >= 11 && OpenSsl.isTlsv13Supported()) {
params.add(new Object[] { type, ProtocolCipherCombo.tlsv13() });
}
} }
return params; return params;
} }
public OpenSslEngineTest(BufferType type) { public OpenSslEngineTest(BufferType type, ProtocolCipherCombo cipherCombo) {
super(type); super(type, cipherCombo);
} }
@BeforeClass @BeforeClass
@ -206,13 +210,17 @@ public class OpenSslEngineTest extends SSLEngineTest {
@Test @Test
public void testWrapBuffersNoWritePendingError() throws Exception { public void testWrapBuffersNoWritePendingError() throws Exception {
clientSslCtx = SslContextBuilder.forClient() clientSslCtx = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE) .trustManager(InsecureTrustManagerFactory.INSTANCE)
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.build(); .protocols(protocols())
.ciphers(ciphers())
.build();
SelfSignedCertificate ssc = new SelfSignedCertificate(); SelfSignedCertificate ssc = new SelfSignedCertificate();
serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.build(); .protocols(protocols())
.ciphers(ciphers())
.build();
SSLEngine clientEngine = null; SSLEngine clientEngine = null;
SSLEngine serverEngine = null; SSLEngine serverEngine = null;
try { try {
@ -240,13 +248,17 @@ public class OpenSslEngineTest extends SSLEngineTest {
@Test @Test
public void testOnlySmallBufferNeededForWrap() throws Exception { public void testOnlySmallBufferNeededForWrap() throws Exception {
clientSslCtx = SslContextBuilder.forClient() clientSslCtx = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE) .trustManager(InsecureTrustManagerFactory.INSTANCE)
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.build(); .protocols(protocols())
.ciphers(ciphers())
.build();
SelfSignedCertificate ssc = new SelfSignedCertificate(); SelfSignedCertificate ssc = new SelfSignedCertificate();
serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.build(); .protocols(protocols())
.ciphers(ciphers())
.build();
SSLEngine clientEngine = null; SSLEngine clientEngine = null;
SSLEngine serverEngine = null; SSLEngine serverEngine = null;
try { try {
@ -291,13 +303,17 @@ public class OpenSslEngineTest extends SSLEngineTest {
@Test @Test
public void testNeededDstCapacityIsCorrectlyCalculated() throws Exception { public void testNeededDstCapacityIsCorrectlyCalculated() throws Exception {
clientSslCtx = SslContextBuilder.forClient() clientSslCtx = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE) .trustManager(InsecureTrustManagerFactory.INSTANCE)
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.build(); .protocols(protocols())
.ciphers(ciphers())
.build();
SelfSignedCertificate ssc = new SelfSignedCertificate(); SelfSignedCertificate ssc = new SelfSignedCertificate();
serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.build(); .protocols(protocols())
.ciphers(ciphers())
.build();
SSLEngine clientEngine = null; SSLEngine clientEngine = null;
SSLEngine serverEngine = null; SSLEngine serverEngine = null;
try { try {
@ -327,13 +343,17 @@ public class OpenSslEngineTest extends SSLEngineTest {
@Test @Test
public void testSrcsLenOverFlowCorrectlyHandled() throws Exception { public void testSrcsLenOverFlowCorrectlyHandled() throws Exception {
clientSslCtx = SslContextBuilder.forClient() clientSslCtx = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE) .trustManager(InsecureTrustManagerFactory.INSTANCE)
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.build(); .protocols(protocols())
.ciphers(ciphers())
.build();
SelfSignedCertificate ssc = new SelfSignedCertificate(); SelfSignedCertificate ssc = new SelfSignedCertificate();
serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.build(); .protocols(protocols())
.ciphers(ciphers())
.build();
SSLEngine clientEngine = null; SSLEngine clientEngine = null;
SSLEngine serverEngine = null; SSLEngine serverEngine = null;
try { try {
@ -374,9 +394,11 @@ public class OpenSslEngineTest extends SSLEngineTest {
@Test @Test
public void testCalculateOutNetBufSizeOverflow() throws SSLException { public void testCalculateOutNetBufSizeOverflow() throws SSLException {
clientSslCtx = SslContextBuilder.forClient() clientSslCtx = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE) .trustManager(InsecureTrustManagerFactory.INSTANCE)
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.build(); .protocols(protocols())
.ciphers(ciphers())
.build();
SSLEngine clientEngine = null; SSLEngine clientEngine = null;
try { try {
clientEngine = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); clientEngine = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT);
@ -390,9 +412,11 @@ public class OpenSslEngineTest extends SSLEngineTest {
@Test @Test
public void testCalculateOutNetBufSize0() throws SSLException { public void testCalculateOutNetBufSize0() throws SSLException {
clientSslCtx = SslContextBuilder.forClient() clientSslCtx = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE) .trustManager(InsecureTrustManagerFactory.INSTANCE)
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.build(); .protocols(protocols())
.ciphers(ciphers())
.build();
SSLEngine clientEngine = null; SSLEngine clientEngine = null;
try { try {
clientEngine = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); clientEngine = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT);
@ -415,13 +439,17 @@ public class OpenSslEngineTest extends SSLEngineTest {
private void testCorrectlyCalculateSpaceForAlert(boolean jdkCompatabilityMode) throws Exception { private void testCorrectlyCalculateSpaceForAlert(boolean jdkCompatabilityMode) throws Exception {
SelfSignedCertificate ssc = new SelfSignedCertificate(); SelfSignedCertificate ssc = new SelfSignedCertificate();
serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.build(); .protocols(protocols())
.ciphers(ciphers())
.build();
clientSslCtx = SslContextBuilder.forClient() clientSslCtx = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE) .trustManager(InsecureTrustManagerFactory.INSTANCE)
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.build(); .protocols(protocols())
.ciphers(ciphers())
.build();
SSLEngine clientEngine = null; SSLEngine clientEngine = null;
SSLEngine serverEngine = null; SSLEngine serverEngine = null;
try { try {
@ -473,13 +501,13 @@ public class OpenSslEngineTest extends SSLEngineTest {
@Test @Test
public void testWrapWithDifferentSizesTLSv1() throws Exception { public void testWrapWithDifferentSizesTLSv1() throws Exception {
clientSslCtx = SslContextBuilder.forClient() clientSslCtx = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE) .trustManager(InsecureTrustManagerFactory.INSTANCE)
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.build(); .build();
SelfSignedCertificate ssc = new SelfSignedCertificate(); SelfSignedCertificate ssc = new SelfSignedCertificate();
serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.build(); .build();
testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "AES128-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "AES128-SHA");
testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "ECDHE-RSA-AES128-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "ECDHE-RSA-AES128-SHA");
@ -504,13 +532,13 @@ public class OpenSslEngineTest extends SSLEngineTest {
@Test @Test
public void testWrapWithDifferentSizesTLSv1_1() throws Exception { public void testWrapWithDifferentSizesTLSv1_1() throws Exception {
clientSslCtx = SslContextBuilder.forClient() clientSslCtx = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE) .trustManager(InsecureTrustManagerFactory.INSTANCE)
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.build(); .build();
SelfSignedCertificate ssc = new SelfSignedCertificate(); SelfSignedCertificate ssc = new SelfSignedCertificate();
serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.build(); .build();
testWrapWithDifferentSizes(PROTOCOL_TLS_V1_1, "ECDHE-RSA-AES256-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_1, "ECDHE-RSA-AES256-SHA");
testWrapWithDifferentSizes(PROTOCOL_TLS_V1_1, "AES256-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_1, "AES256-SHA");
@ -613,12 +641,16 @@ public class OpenSslEngineTest extends SSLEngineTest {
.forClient() .forClient()
.trustManager(cert.cert()) .trustManager(cert.cert())
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine());
serverSslCtx = SslContextBuilder serverSslCtx = SslContextBuilder
.forServer(cert.certificate(), cert.privateKey()) .forServer(cert.certificate(), cert.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine server = wrapEngine(serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); SSLEngine server = wrapEngine(serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine());
@ -690,12 +722,16 @@ public class OpenSslEngineTest extends SSLEngineTest {
.forClient() .forClient()
.trustManager(cert.cert()) .trustManager(cert.cert())
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine());
serverSslCtx = SslContextBuilder serverSslCtx = SslContextBuilder
.forServer(cert.certificate(), cert.privateKey()) .forServer(cert.certificate(), cert.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine server = wrapEngine(serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); SSLEngine server = wrapEngine(serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine());
@ -774,12 +810,16 @@ public class OpenSslEngineTest extends SSLEngineTest {
.forClient() .forClient()
.trustManager(cert.cert()) .trustManager(cert.cert())
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine());
serverSslCtx = SslContextBuilder serverSslCtx = SslContextBuilder
.forServer(cert.certificate(), cert.privateKey()) .forServer(cert.certificate(), cert.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine server = wrapEngine(serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); SSLEngine server = wrapEngine(serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine());
@ -849,12 +889,16 @@ public class OpenSslEngineTest extends SSLEngineTest {
.forClient() .forClient()
.trustManager(cert.cert()) .trustManager(cert.cert())
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine());
serverSslCtx = SslContextBuilder serverSslCtx = SslContextBuilder
.forServer(cert.certificate(), cert.privateKey()) .forServer(cert.certificate(), cert.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine server = wrapEngine(serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); SSLEngine server = wrapEngine(serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine());
@ -982,8 +1026,10 @@ public class OpenSslEngineTest extends SSLEngineTest {
assumeTrue(PlatformDependent.javaVersion() >= 8); assumeTrue(PlatformDependent.javaVersion() >= 8);
SelfSignedCertificate ssc = new SelfSignedCertificate(); SelfSignedCertificate ssc = new SelfSignedCertificate();
serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.build(); .protocols(protocols())
.ciphers(ciphers())
.build();
SSLEngine engine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); SSLEngine engine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
try { try {
@ -1002,8 +1048,10 @@ public class OpenSslEngineTest extends SSLEngineTest {
byte[] name = "rb8hx3pww30y3tvw0mwy.v1_1".getBytes(CharsetUtil.UTF_8); byte[] name = "rb8hx3pww30y3tvw0mwy.v1_1".getBytes(CharsetUtil.UTF_8);
SelfSignedCertificate ssc = new SelfSignedCertificate(); SelfSignedCertificate ssc = new SelfSignedCertificate();
serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.build(); .protocols(protocols())
.ciphers(ciphers())
.build();
SSLEngine engine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); SSLEngine engine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
try { try {
@ -1022,8 +1070,10 @@ public class OpenSslEngineTest extends SSLEngineTest {
public void testAlgorithmConstraintsThrows() throws Exception { public void testAlgorithmConstraintsThrows() throws Exception {
SelfSignedCertificate ssc = new SelfSignedCertificate(); SelfSignedCertificate ssc = new SelfSignedCertificate();
serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.build(); .protocols(protocols())
.ciphers(ciphers())
.build();
SSLEngine engine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); SSLEngine engine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
try { try {

View File

@ -15,6 +15,7 @@
*/ */
package io.netty.handler.ssl; package io.netty.handler.ssl;
import io.netty.util.internal.PlatformDependent;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
@ -34,17 +35,21 @@ import static org.junit.Assume.assumeTrue;
@RunWith(Parameterized.class) @RunWith(Parameterized.class)
public class OpenSslJdkSslEngineInteroptTest extends SSLEngineTest { public class OpenSslJdkSslEngineInteroptTest extends SSLEngineTest {
@Parameterized.Parameters(name = "{index}: bufferType = {0}") @Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}")
public static Collection<Object> data() { public static Collection<Object[]> data() {
List<Object> params = new ArrayList<Object>(); List<Object[]> params = new ArrayList<Object[]>();
for (BufferType type: BufferType.values()) { for (BufferType type: BufferType.values()) {
params.add(type); params.add(new Object[] { type, ProtocolCipherCombo.tlsv12()});
if (PlatformDependent.javaVersion() >= 11 && OpenSsl.isTlsv13Supported()) {
params.add(new Object[] { type, ProtocolCipherCombo.tlsv13() });
}
} }
return params; return params;
} }
public OpenSslJdkSslEngineInteroptTest(BufferType type) { public OpenSslJdkSslEngineInteroptTest(BufferType type, ProtocolCipherCombo combo) {
super(type); super(type, combo);
} }
@BeforeClass @BeforeClass

View File

@ -15,6 +15,12 @@
*/ */
package io.netty.handler.ssl; package io.netty.handler.ssl;
import io.netty.util.internal.PlatformDependent;
import java.util.Arrays;
import java.util.Collections;
import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_2;
import static org.junit.Assume.assumeTrue; import static org.junit.Assume.assumeTrue;
final class OpenSslTestUtils { final class OpenSslTestUtils {
@ -28,4 +34,17 @@ final class OpenSslTestUtils {
static boolean isBoringSSL() { static boolean isBoringSSL() {
return "BoringSSL".equals(OpenSsl.versionString()); return "BoringSSL".equals(OpenSsl.versionString());
} }
static SslContextBuilder configureProtocolForMutualAuth(
SslContextBuilder ctx, SslProvider sslClientProvider, SslProvider sslServerProvider) {
if (PlatformDependent.javaVersion() >= 11
&& sslClientProvider == SslProvider.JDK && sslServerProvider != SslProvider.JDK) {
// Make sure we do not use TLSv1.3 as there seems to be a bug currently in the JDK TLSv1.3 implementation.
// See:
// - http://mail.openjdk.java.net/pipermail/security-dev/2018-September/018191.html
// - https://bugs.openjdk.java.net/projects/JDK/issues/JDK-8210846
ctx.protocols(PROTOCOL_TLS_V1_2).ciphers(Collections.singleton("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"));
}
return ctx;
}
} }

View File

@ -381,12 +381,21 @@ public class ParameterizedSslHandlerTest {
SelfSignedCertificate ssc = new SelfSignedCertificate(); SelfSignedCertificate ssc = new SelfSignedCertificate();
final SslContext sslServerCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) final SslContext sslServerCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
.sslProvider(serverProvider) .sslProvider(serverProvider)
.build(); // Use TLSv1.2 as we depend on the fact that the handshake
// is done in an extra round trip in the test which
// is not true in TLSv1.3
.protocols(SslUtils.PROTOCOL_TLS_V1_2)
.build();
final SslContext sslClientCtx = SslContextBuilder.forClient() final SslContext sslClientCtx = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE) .trustManager(InsecureTrustManagerFactory.INSTANCE)
.sslProvider(clientProvider).build(); .sslProvider(clientProvider)
// Use TLSv1.2 as we depend on the fact that the handshake
// is done in an extra round trip in the test which
// is not true in TLSv1.3
.protocols(SslUtils.PROTOCOL_TLS_V1_2)
.build();
EventLoopGroup group = new NioEventLoopGroup(); EventLoopGroup group = new NioEventLoopGroup();
Channel sc = null; Channel sc = null;

View File

@ -23,8 +23,8 @@ import javax.net.ssl.SSLEngine;
public class ReferenceCountedOpenSslEngineTest extends OpenSslEngineTest { public class ReferenceCountedOpenSslEngineTest extends OpenSslEngineTest {
public ReferenceCountedOpenSslEngineTest(BufferType type) { public ReferenceCountedOpenSslEngineTest(BufferType type, ProtocolCipherCombo combo) {
super(type); super(type, combo);
} }
@Override @Override
@ -60,9 +60,11 @@ public class ReferenceCountedOpenSslEngineTest extends OpenSslEngineTest {
@Test(expected = NullPointerException.class) @Test(expected = NullPointerException.class)
public void testNotLeakOnException() throws Exception { public void testNotLeakOnException() throws Exception {
clientSslCtx = SslContextBuilder.forClient() clientSslCtx = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE) .trustManager(InsecureTrustManagerFactory.INSTANCE)
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.build(); .protocols(protocols())
.ciphers(ciphers())
.build();
clientSslCtx.newEngine(null); clientSslCtx.newEngine(null);
} }

View File

@ -33,6 +33,7 @@ import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.handler.ssl.util.SelfSignedCertificate; import io.netty.handler.ssl.util.SelfSignedCertificate;
import io.netty.handler.ssl.util.SimpleTrustManagerFactory; import io.netty.handler.ssl.util.SimpleTrustManagerFactory;
@ -67,6 +68,7 @@ import java.security.Provider;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -79,6 +81,7 @@ import javax.net.ssl.ManagerFactoryParameters;
import javax.net.ssl.SNIHostName; import javax.net.ssl.SNIHostName;
import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLEngineResult.Status;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLParameters;
@ -89,14 +92,7 @@ import javax.net.ssl.TrustManagerFactorySpi;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
import javax.security.cert.X509Certificate; import javax.security.cert.X509Certificate;
import static io.netty.handler.ssl.SslUtils.PROTOCOL_SSL_V2; import static io.netty.handler.ssl.SslUtils.*;
import static io.netty.handler.ssl.SslUtils.PROTOCOL_SSL_V2_HELLO;
import static io.netty.handler.ssl.SslUtils.PROTOCOL_SSL_V3;
import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1;
import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_1;
import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_2;
import static io.netty.handler.ssl.SslUtils.SSL_RECORD_HEADER_LENGTH;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
@ -220,10 +216,42 @@ public abstract class SSLEngineTest {
Mixed Mixed
} }
private final BufferType type; static final class ProtocolCipherCombo {
private static final ProtocolCipherCombo TLSV12 = new ProtocolCipherCombo(
PROTOCOL_TLS_V1_2, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
private static final ProtocolCipherCombo TLSV13 = new ProtocolCipherCombo(
PROTOCOL_TLS_V1_3, "TLS_AES_128_GCM_SHA256");
final String protocol;
final String cipher;
protected SSLEngineTest(BufferType type) { private ProtocolCipherCombo(String protocol, String cipher) {
this.protocol = protocol;
this.cipher = cipher;
}
static ProtocolCipherCombo tlsv12() {
return TLSV12;
}
static ProtocolCipherCombo tlsv13() {
return TLSV13;
}
@Override
public String toString() {
return "ProtocolCipherCombo{" +
"protocol='" + protocol + '\'' +
", cipher='" + cipher + '\'' +
'}';
}
}
private final BufferType type;
private final ProtocolCipherCombo protocolCipherCombo;
protected SSLEngineTest(BufferType type, ProtocolCipherCombo protocolCipherCombo) {
this.type = type; this.type = type;
this.protocolCipherCombo = protocolCipherCombo;
} }
protected ByteBuffer allocateBuffer(int len) { protected ByteBuffer allocateBuffer(int len) {
@ -620,36 +648,46 @@ public abstract class SSLEngineTest {
} }
protected boolean mySetupMutualAuthServerIsValidException(Throwable cause) { protected boolean mySetupMutualAuthServerIsValidException(Throwable cause) {
return cause instanceof SSLHandshakeException || cause instanceof ClosedChannelException; // As in TLSv1.3 the handshake is sent without an extra roundtrip an SSLException is valid as well.
return cause instanceof SSLException || cause instanceof ClosedChannelException;
} }
protected void mySetupMutualAuthServerInitSslHandler(SslHandler handler) { protected void mySetupMutualAuthServerInitSslHandler(SslHandler handler) {
} }
private SslContextBuilder configureProtocolForMutualAuth(SslContextBuilder ctx) {
return OpenSslTestUtils.configureProtocolForMutualAuth(ctx, sslClientProvider(), sslServerProvider());
}
private void mySetupMutualAuth(KeyManagerFactory serverKMF, final File serverTrustManager, private void mySetupMutualAuth(KeyManagerFactory serverKMF, final File serverTrustManager,
KeyManagerFactory clientKMF, File clientTrustManager, KeyManagerFactory clientKMF, File clientTrustManager,
ClientAuth clientAuth, final boolean failureExpected, ClientAuth clientAuth, final boolean failureExpected,
final boolean serverInitEngine) final boolean serverInitEngine)
throws SSLException, InterruptedException { throws SSLException, InterruptedException {
serverSslCtx = SslContextBuilder.forServer(serverKMF) serverSslCtx = configureProtocolForMutualAuth(
.sslProvider(sslServerProvider()) SslContextBuilder.forServer(serverKMF)
.sslContextProvider(serverSslContextProvider()) .protocols(protocols())
.trustManager(serverTrustManager) .ciphers(ciphers())
.clientAuth(clientAuth) .sslProvider(sslServerProvider())
.ciphers(null, IdentityCipherSuiteFilter.INSTANCE) .sslContextProvider(serverSslContextProvider())
.sessionCacheSize(0) .trustManager(serverTrustManager)
.sessionTimeout(0) .clientAuth(clientAuth)
.build(); .ciphers(null, IdentityCipherSuiteFilter.INSTANCE)
.sessionCacheSize(0)
.sessionTimeout(0)).build();
clientSslCtx = configureProtocolForMutualAuth(
SslContextBuilder.forClient()
.protocols(protocols())
.ciphers(ciphers())
.sslProvider(sslClientProvider())
.sslContextProvider(clientSslContextProvider())
.trustManager(clientTrustManager)
.keyManager(clientKMF)
.ciphers(null, IdentityCipherSuiteFilter.INSTANCE)
.sessionCacheSize(0)
.sessionTimeout(0)).build();
clientSslCtx = SslContextBuilder.forClient()
.sslProvider(sslClientProvider())
.sslContextProvider(clientSslContextProvider())
.trustManager(clientTrustManager)
.keyManager(clientKMF)
.ciphers(null, IdentityCipherSuiteFilter.INSTANCE)
.sessionCacheSize(0)
.sessionTimeout(0)
.build();
serverConnectedChannel = null; serverConnectedChannel = null;
sb = new ServerBootstrap(); sb = new ServerBootstrap();
cb = new Bootstrap(); cb = new Bootstrap();
@ -711,10 +749,11 @@ public abstract class SSLEngineTest {
@Override @Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt == SslHandshakeCompletionEvent.SUCCESS) { if (evt == SslHandshakeCompletionEvent.SUCCESS) {
if (failureExpected) { // With TLS1.3 a mutal auth error will not be propagated as a handshake error most of the
clientException = new IllegalStateException("handshake complete. expected failure"); // time as the handshake needs NO extra roundtrip.
if (!failureExpected) {
clientLatch.countDown();
} }
clientLatch.countDown();
} else if (evt instanceof SslHandshakeCompletionEvent) { } else if (evt instanceof SslHandshakeCompletionEvent) {
clientException = ((SslHandshakeCompletionEvent) evt).cause(); clientException = ((SslHandshakeCompletionEvent) evt).cause();
clientLatch.countDown(); clientLatch.countDown();
@ -724,7 +763,7 @@ public abstract class SSLEngineTest {
@Override @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (cause.getCause() instanceof SSLHandshakeException) { if (cause.getCause() instanceof SSLException) {
clientException = cause.getCause(); clientException = cause.getCause();
clientLatch.countDown(); clientLatch.countDown();
} else { } else {
@ -735,7 +774,7 @@ public abstract class SSLEngineTest {
} }
}); });
serverChannel = sb.bind(new InetSocketAddress(0)).sync().channel(); serverChannel = sb.bind(new InetSocketAddress(8443)).sync().channel();
int port = ((InetSocketAddress) serverChannel.localAddress()).getPort(); int port = ((InetSocketAddress) serverChannel.localAddress()).getPort();
ChannelFuture ccf = cb.connect(new InetSocketAddress(NetUtil.LOCALHOST, port)); ChannelFuture ccf = cb.connect(new InetSocketAddress(NetUtil.LOCALHOST, port));
@ -776,6 +815,8 @@ public abstract class SSLEngineTest {
final String expectedHost = "localhost"; final String expectedHost = "localhost";
serverSslCtx = SslContextBuilder.forServer(serverCrtFile, serverKeyFile, null) serverSslCtx = SslContextBuilder.forServer(serverCrtFile, serverKeyFile, null)
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.protocols(protocols())
.ciphers(ciphers())
.sslContextProvider(serverSslContextProvider()) .sslContextProvider(serverSslContextProvider())
.trustManager(InsecureTrustManagerFactory.INSTANCE) .trustManager(InsecureTrustManagerFactory.INSTANCE)
.ciphers(null, IdentityCipherSuiteFilter.INSTANCE) .ciphers(null, IdentityCipherSuiteFilter.INSTANCE)
@ -785,12 +826,15 @@ public abstract class SSLEngineTest {
clientSslCtx = SslContextBuilder.forClient() clientSslCtx = SslContextBuilder.forClient()
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.protocols(protocols())
.ciphers(ciphers())
.sslContextProvider(clientSslContextProvider()) .sslContextProvider(clientSslContextProvider())
.trustManager(clientTrustCrtFile) .trustManager(clientTrustCrtFile)
.ciphers(null, IdentityCipherSuiteFilter.INSTANCE) .ciphers(null, IdentityCipherSuiteFilter.INSTANCE)
.sessionCacheSize(0) .sessionCacheSize(0)
.sessionTimeout(0) .sessionTimeout(0)
.build(); .build();
serverConnectedChannel = null; serverConnectedChannel = null;
sb = new ServerBootstrap(); sb = new ServerBootstrap();
cb = new Bootstrap(); cb = new Bootstrap();
@ -897,24 +941,28 @@ public abstract class SSLEngineTest {
File servertTrustCrtFile, File serverKeyFile, final File serverCrtFile, String serverKeyPassword, File servertTrustCrtFile, File serverKeyFile, final File serverCrtFile, String serverKeyPassword,
File clientTrustCrtFile, File clientKeyFile, File clientCrtFile, String clientKeyPassword) File clientTrustCrtFile, File clientKeyFile, File clientCrtFile, String clientKeyPassword)
throws InterruptedException, SSLException { throws InterruptedException, SSLException {
serverSslCtx = SslContextBuilder.forServer(serverCrtFile, serverKeyFile, serverKeyPassword) serverSslCtx = configureProtocolForMutualAuth(
.sslProvider(sslServerProvider()) SslContextBuilder.forServer(serverCrtFile, serverKeyFile, serverKeyPassword)
.sslContextProvider(serverSslContextProvider()) .sslProvider(sslServerProvider())
.trustManager(servertTrustCrtFile) .sslContextProvider(serverSslContextProvider())
.ciphers(null, IdentityCipherSuiteFilter.INSTANCE) .protocols(protocols())
.sessionCacheSize(0) .ciphers(ciphers())
.sessionTimeout(0) .trustManager(servertTrustCrtFile)
.build(); .ciphers(null, IdentityCipherSuiteFilter.INSTANCE)
.sessionCacheSize(0)
.sessionTimeout(0)).build();
clientSslCtx = configureProtocolForMutualAuth(
SslContextBuilder.forClient()
.sslProvider(sslClientProvider())
.sslContextProvider(clientSslContextProvider())
.protocols(protocols())
.ciphers(ciphers())
.trustManager(clientTrustCrtFile)
.keyManager(clientCrtFile, clientKeyFile, clientKeyPassword)
.ciphers(null, IdentityCipherSuiteFilter.INSTANCE)
.sessionCacheSize(0)
.sessionTimeout(0)).build();
clientSslCtx = SslContextBuilder.forClient()
.sslProvider(sslClientProvider())
.sslContextProvider(clientSslContextProvider())
.trustManager(clientTrustCrtFile)
.keyManager(clientCrtFile, clientKeyFile, clientKeyPassword)
.ciphers(null, IdentityCipherSuiteFilter.INSTANCE)
.sessionCacheSize(0)
.sessionTimeout(0)
.build();
serverConnectedChannel = null; serverConnectedChannel = null;
sb = new ServerBootstrap(); sb = new ServerBootstrap();
cb = new Bootstrap(); cb = new Bootstrap();
@ -930,6 +978,7 @@ public abstract class SSLEngineTest {
SSLEngine engine = serverSslCtx.newEngine(ch.alloc()); SSLEngine engine = serverSslCtx.newEngine(ch.alloc());
engine.setUseClientMode(false); engine.setUseClientMode(false);
engine.setNeedClientAuth(true); engine.setNeedClientAuth(true);
p.addLast(new SslHandler(engine)); p.addLast(new SslHandler(engine));
p.addLast(new MessageDelegatorChannelHandler(serverReceiver, serverLatch)); p.addLast(new MessageDelegatorChannelHandler(serverReceiver, serverLatch));
p.addLast(new ChannelInboundHandlerAdapter() { p.addLast(new ChannelInboundHandlerAdapter() {
@ -986,13 +1035,14 @@ public abstract class SSLEngineTest {
protected void initChannel(Channel ch) throws Exception { protected void initChannel(Channel ch) throws Exception {
ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), type)); ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), type));
SslHandler handler = clientSslCtx.newHandler(ch.alloc());
handler.engine().setNeedClientAuth(true);
ChannelPipeline p = ch.pipeline(); ChannelPipeline p = ch.pipeline();
p.addLast(clientSslCtx.newHandler(ch.alloc())); p.addLast(handler);
p.addLast(new MessageDelegatorChannelHandler(clientReceiver, clientLatch)); p.addLast(new MessageDelegatorChannelHandler(clientReceiver, clientLatch));
p.addLast(new ChannelInboundHandlerAdapter() { p.addLast(new ChannelInboundHandlerAdapter() {
@Override @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
if (cause.getCause() instanceof SSLHandshakeException) { if (cause.getCause() instanceof SSLHandshakeException) {
clientException = cause.getCause(); clientException = cause.getCause();
clientLatch.countDown(); clientLatch.countDown();
@ -1081,11 +1131,15 @@ public abstract class SSLEngineTest {
.trustManager(InsecureTrustManagerFactory.INSTANCE) .trustManager(InsecureTrustManagerFactory.INSTANCE)
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.sslContextProvider(clientSslContextProvider()) .sslContextProvider(clientSslContextProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SelfSignedCertificate ssc = new SelfSignedCertificate(); SelfSignedCertificate ssc = new SelfSignedCertificate();
serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.sslContextProvider(serverSslContextProvider()) .sslContextProvider(serverSslContextProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine clientEngine = null; SSLEngine clientEngine = null;
SSLEngine serverEngine = null; SSLEngine serverEngine = null;
@ -1110,11 +1164,15 @@ public abstract class SSLEngineTest {
clientSslCtx = SslContextBuilder.forClient() clientSslCtx = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE) .trustManager(InsecureTrustManagerFactory.INSTANCE)
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
// This test only works for non TLSv1.3 for now
.protocols(PROTOCOL_TLS_V1_2)
.sslContextProvider(clientSslContextProvider()) .sslContextProvider(clientSslContextProvider())
.build(); .build();
SelfSignedCertificate ssc = new SelfSignedCertificate(); SelfSignedCertificate ssc = new SelfSignedCertificate();
serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
// This test only works for non TLSv1.3 for now
.protocols(PROTOCOL_TLS_V1_2)
.sslContextProvider(serverSslContextProvider()) .sslContextProvider(serverSslContextProvider())
.build(); .build();
SSLEngine clientEngine = null; SSLEngine clientEngine = null;
@ -1145,8 +1203,11 @@ public abstract class SSLEngineTest {
throws CertificateException, SSLException, InterruptedException, ExecutionException { throws CertificateException, SSLException, InterruptedException, ExecutionException {
final SelfSignedCertificate ssc = new SelfSignedCertificate(); final SelfSignedCertificate ssc = new SelfSignedCertificate();
serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.sslContextProvider(serverSslContextProvider()).build(); .sslContextProvider(serverSslContextProvider())
.protocols(protocols())
.ciphers(ciphers())
.build();
sb = new ServerBootstrap() sb = new ServerBootstrap()
.group(new NioEventLoopGroup(1)) .group(new NioEventLoopGroup(1))
.channel(NioServerSocketChannel.class) .channel(NioServerSocketChannel.class)
@ -1196,8 +1257,12 @@ public abstract class SSLEngineTest {
serverChannel = sb.bind(new InetSocketAddress(0)).syncUninterruptibly().channel(); serverChannel = sb.bind(new InetSocketAddress(0)).syncUninterruptibly().channel();
clientSslCtx = SslContextBuilder.forClient() clientSslCtx = SslContextBuilder.forClient()
.sslProvider(SslProvider.JDK) // OpenSslEngine doesn't support renegotiation on client side // OpenSslEngine doesn't support renegotiation on client side
.trustManager(InsecureTrustManagerFactory.INSTANCE).build(); .sslProvider(SslProvider.JDK)
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.protocols(protocols())
.ciphers(ciphers())
.build();
cb = new Bootstrap(); cb = new Bootstrap();
cb.group(new NioEventLoopGroup(1)) cb.group(new NioEventLoopGroup(1))
@ -1257,9 +1322,11 @@ public abstract class SSLEngineTest {
File serverKeyFile = new File(getClass().getResource("test_unencrypted.pem").getFile()); File serverKeyFile = new File(getClass().getResource("test_unencrypted.pem").getFile());
File serverCrtFile = new File(getClass().getResource("test.crt").getFile()); File serverCrtFile = new File(getClass().getResource("test.crt").getFile());
serverSslCtx = SslContextBuilder.forServer(serverCrtFile, serverKeyFile) serverSslCtx = SslContextBuilder.forServer(serverCrtFile, serverKeyFile)
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.sslContextProvider(serverSslContextProvider()) .sslContextProvider(serverSslContextProvider())
.build(); .protocols(protocols())
.ciphers(ciphers())
.build();
sslEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); sslEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
@ -1338,7 +1405,10 @@ public abstract class SSLEngineTest {
cTOsPos = cTOs.position(); cTOsPos = cTOs.position();
sTOcPos = sTOc.position(); sTOcPos = sTOc.position();
if (!clientHandshakeFinished) { if (!clientHandshakeFinished ||
// After the handshake completes it is possible we have more data that was send by the server as
// the server will send session updates after the handshake. In this case continue to unwrap.
SslUtils.PROTOCOL_TLS_V1_3.equals(clientEngine.getSession().getProtocol())) {
int clientAppReadBufferPos = clientAppReadBuffer.position(); int clientAppReadBufferPos = clientAppReadBuffer.position();
clientResult = clientEngine.unwrap(sTOc, clientAppReadBuffer); clientResult = clientEngine.unwrap(sTOc, clientAppReadBuffer);
@ -1350,7 +1420,7 @@ public abstract class SSLEngineTest {
clientHandshakeFinished = true; clientHandshakeFinished = true;
} }
} else { } else {
assertFalse(sTOc.hasRemaining()); assertEquals(0, sTOc.remaining());
} }
if (!serverHandshakeFinished) { if (!serverHandshakeFinished) {
@ -1433,24 +1503,35 @@ public abstract class SSLEngineTest {
SelfSignedCertificate ssc = new SelfSignedCertificate(); SelfSignedCertificate ssc = new SelfSignedCertificate();
try { try {
setupHandlers(SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey(), null) SslContextBuilder serverCtxBuilder = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey(), null)
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.sslContextProvider(serverSslContextProvider()) .sslContextProvider(serverSslContextProvider())
.ciphers(null, IdentityCipherSuiteFilter.INSTANCE) .ciphers(null, IdentityCipherSuiteFilter.INSTANCE)
.applicationProtocolConfig(serverApn) .applicationProtocolConfig(serverApn)
.sessionCacheSize(0) .sessionCacheSize(0)
.sessionTimeout(0) .sessionTimeout(0);
.build(), if (serverApn.protocol() == Protocol.NPN || serverApn.protocol() == Protocol.NPN_AND_ALPN) {
// NPN is not really well supported with TLSv1.3 so force to use TLSv1.2
// See https://github.com/openssl/openssl/issues/3665
serverCtxBuilder.protocols(PROTOCOL_TLS_V1_2);
}
SslContextBuilder.forClient() SslContextBuilder clientCtxBuilder = SslContextBuilder.forClient()
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.sslContextProvider(clientSslContextProvider()) .sslContextProvider(clientSslContextProvider())
.applicationProtocolConfig(clientApn) .applicationProtocolConfig(clientApn)
.trustManager(InsecureTrustManagerFactory.INSTANCE) .trustManager(InsecureTrustManagerFactory.INSTANCE)
.ciphers(null, IdentityCipherSuiteFilter.INSTANCE) .ciphers(null, IdentityCipherSuiteFilter.INSTANCE)
.sessionCacheSize(0) .sessionCacheSize(0)
.sessionTimeout(0) .sessionTimeout(0);
.build());
if (clientApn.protocol() == Protocol.NPN || clientApn.protocol() == Protocol.NPN_AND_ALPN) {
// NPN is not really well supported with TLSv1.3 so force to use TLSv1.2
// See https://github.com/openssl/openssl/issues/3665
clientCtxBuilder.protocols(PROTOCOL_TLS_V1_2);
}
setupHandlers(serverCtxBuilder.build(), clientCtxBuilder.build());
} finally { } finally {
ssc.delete(); ssc.delete();
} }
@ -1511,6 +1592,11 @@ public abstract class SSLEngineTest {
ctx.fireExceptionCaught(cause); ctx.fireExceptionCaught(cause);
} }
} }
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
clientLatch.countDown();
}
}); });
} }
}); });
@ -1524,12 +1610,15 @@ public abstract class SSLEngineTest {
@Test(timeout = 30000) @Test(timeout = 30000)
public void testMutualAuthSameCertChain() throws Exception { public void testMutualAuthSameCertChain() throws Exception {
serverSslCtx = SslContextBuilder.forServer( serverSslCtx = configureProtocolForMutualAuth(
new ByteArrayInputStream(X509_CERT_PEM.getBytes(CharsetUtil.UTF_8)), SslContextBuilder.forServer(
new ByteArrayInputStream(PRIVATE_KEY_PEM.getBytes(CharsetUtil.UTF_8))) new ByteArrayInputStream(X509_CERT_PEM.getBytes(CharsetUtil.UTF_8)),
.trustManager(new ByteArrayInputStream(X509_CERT_PEM.getBytes(CharsetUtil.UTF_8))) new ByteArrayInputStream(PRIVATE_KEY_PEM.getBytes(CharsetUtil.UTF_8)))
.clientAuth(ClientAuth.REQUIRE).sslProvider(sslServerProvider()) .trustManager(new ByteArrayInputStream(X509_CERT_PEM.getBytes(CharsetUtil.UTF_8)))
.sslContextProvider(serverSslContextProvider()).build(); .clientAuth(ClientAuth.REQUIRE).sslProvider(sslServerProvider())
.sslContextProvider(serverSslContextProvider())
.protocols(protocols())
.ciphers(ciphers())).build();
sb = new ServerBootstrap(); sb = new ServerBootstrap();
sb.group(new NioEventLoopGroup(), new NioEventLoopGroup()); sb.group(new NioEventLoopGroup(), new NioEventLoopGroup());
@ -1580,13 +1669,14 @@ public abstract class SSLEngineTest {
} }
}).bind(new InetSocketAddress(0)).syncUninterruptibly().channel(); }).bind(new InetSocketAddress(0)).syncUninterruptibly().channel();
clientSslCtx = SslContextBuilder.forClient() clientSslCtx = configureProtocolForMutualAuth(
.keyManager( SslContextBuilder.forClient().keyManager(
new ByteArrayInputStream(CLIENT_X509_CERT_CHAIN_PEM.getBytes(CharsetUtil.UTF_8)), new ByteArrayInputStream(CLIENT_X509_CERT_CHAIN_PEM.getBytes(CharsetUtil.UTF_8)),
new ByteArrayInputStream(CLIENT_PRIVATE_KEY_PEM.getBytes(CharsetUtil.UTF_8))) new ByteArrayInputStream(CLIENT_PRIVATE_KEY_PEM.getBytes(CharsetUtil.UTF_8)))
.trustManager(new ByteArrayInputStream(X509_CERT_PEM.getBytes(CharsetUtil.UTF_8))) .trustManager(new ByteArrayInputStream(X509_CERT_PEM.getBytes(CharsetUtil.UTF_8)))
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.sslContextProvider(clientSslContextProvider()).build(); .sslContextProvider(clientSslContextProvider())
.protocols(protocols()).ciphers(ciphers())).build();
cb = new Bootstrap(); cb = new Bootstrap();
cb.group(new NioEventLoopGroup()); cb.group(new NioEventLoopGroup());
cb.channel(NioSocketChannel.class); cb.channel(NioSocketChannel.class);
@ -1610,12 +1700,16 @@ public abstract class SSLEngineTest {
.forClient() .forClient()
.trustManager(cert.cert()) .trustManager(cert.cert())
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
serverSslCtx = SslContextBuilder serverSslCtx = SslContextBuilder
.forServer(cert.certificate(), cert.privateKey()) .forServer(cert.certificate(), cert.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
@ -1790,12 +1884,16 @@ public abstract class SSLEngineTest {
.forClient() .forClient()
.trustManager(cert.cert()) .trustManager(cert.cert())
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
serverSslCtx = SslContextBuilder serverSslCtx = SslContextBuilder
.forServer(cert.certificate(), cert.privateKey()) .forServer(cert.certificate(), cert.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
@ -1829,6 +1927,8 @@ public abstract class SSLEngineTest {
clientSslCtx = SslContextBuilder clientSslCtx = SslContextBuilder
.forClient() .forClient()
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
@ -1857,6 +1957,8 @@ public abstract class SSLEngineTest {
clientSslCtx = SslContextBuilder clientSslCtx = SslContextBuilder
.forClient() .forClient()
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
@ -1881,12 +1983,16 @@ public abstract class SSLEngineTest {
clientSslCtx = SslContextBuilder clientSslCtx = SslContextBuilder
.forClient() .forClient()
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
serverSslCtx = SslContextBuilder serverSslCtx = SslContextBuilder
.forServer(cert.certificate(), cert.privateKey()) .forServer(cert.certificate(), cert.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
@ -1928,12 +2034,16 @@ public abstract class SSLEngineTest {
clientSslCtx = SslContextBuilder clientSslCtx = SslContextBuilder
.forClient() .forClient()
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
serverSslCtx = SslContextBuilder serverSslCtx = SslContextBuilder
.forServer(cert.certificate(), cert.privateKey()) .forServer(cert.certificate(), cert.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
@ -1965,12 +2075,16 @@ public abstract class SSLEngineTest {
.forClient() .forClient()
.trustManager(cert.cert()) .trustManager(cert.cert())
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
// This test only works for non TLSv1.3 for now
.protocols(PROTOCOL_TLS_V1_2)
.build(); .build();
SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
serverSslCtx = SslContextBuilder serverSslCtx = SslContextBuilder
.forServer(cert.certificate(), cert.privateKey()) .forServer(cert.certificate(), cert.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
// This test only works for non TLSv1.3 for now
.protocols(PROTOCOL_TLS_V1_2)
.build(); .build();
SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
@ -2032,6 +2146,7 @@ public abstract class SSLEngineTest {
result = server.wrap(empty, encryptedServerToClient); result = server.wrap(empty, encryptedServerToClient);
encryptedServerToClient.flip(); encryptedServerToClient.flip();
assertEquals(SSLEngineResult.Status.CLOSED, result.getStatus()); assertEquals(SSLEngineResult.Status.CLOSED, result.getStatus());
// UNWRAP/WRAP are not expected after this point // UNWRAP/WRAP are not expected after this point
assertEquals(SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, result.getHandshakeStatus()); assertEquals(SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, result.getHandshakeStatus());
@ -2046,6 +2161,7 @@ public abstract class SSLEngineTest {
assertTrue(server.isInboundDone()); assertTrue(server.isInboundDone());
result = client.unwrap(encryptedServerToClient, plainClientOut); result = client.unwrap(encryptedServerToClient, plainClientOut);
plainClientOut.flip(); plainClientOut.flip();
assertEquals(SSLEngineResult.Status.CLOSED, result.getStatus()); assertEquals(SSLEngineResult.Status.CLOSED, result.getStatus());
// UNWRAP/WRAP are not expected after this point // UNWRAP/WRAP are not expected after this point
@ -2106,12 +2222,16 @@ public abstract class SSLEngineTest {
.forClient() .forClient()
.trustManager(cert.cert()) .trustManager(cert.cert())
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
serverSslCtx = SslContextBuilder serverSslCtx = SslContextBuilder
.forServer(cert.certificate(), cert.privateKey()) .forServer(cert.certificate(), cert.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
@ -2145,12 +2265,16 @@ public abstract class SSLEngineTest {
.forClient() .forClient()
.trustManager(cert.cert()) .trustManager(cert.cert())
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
serverSslCtx = SslContextBuilder serverSslCtx = SslContextBuilder
.forServer(cert.certificate(), cert.privateKey()) .forServer(cert.certificate(), cert.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
@ -2220,12 +2344,16 @@ public abstract class SSLEngineTest {
.forClient() .forClient()
.trustManager(cert.cert()) .trustManager(cert.cert())
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
serverSslCtx = SslContextBuilder serverSslCtx = SslContextBuilder
.forServer(cert.certificate(), cert.privateKey()) .forServer(cert.certificate(), cert.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
@ -2240,21 +2368,35 @@ public abstract class SSLEngineTest {
int srcLen = plainClientOut.remaining(); int srcLen = plainClientOut.remaining();
SSLEngineResult result; SSLEngineResult result;
while (encClientToServer.position() <= server.getSession().getPacketBufferSize()) { int count = 0;
do {
int plainClientOutPosition = plainClientOut.position();
int encClientToServerPosition = encClientToServer.position();
result = client.wrap(plainClientOut, encClientToServer); result = client.wrap(plainClientOut, encClientToServer);
if (result.getStatus() == Status.BUFFER_OVERFLOW) {
// We did not have enough room to wrap
assertEquals(plainClientOutPosition, plainClientOut.position());
assertEquals(encClientToServerPosition, encClientToServer.position());
break;
}
assertEquals(SSLEngineResult.Status.OK, result.getStatus()); assertEquals(SSLEngineResult.Status.OK, result.getStatus());
assertEquals(srcLen, result.bytesConsumed()); assertEquals(srcLen, result.bytesConsumed());
assertTrue(result.bytesProduced() > 0); assertTrue(result.bytesProduced() > 0);
plainClientOut.clear(); plainClientOut.clear();
}
++count;
} while (encClientToServer.position() < server.getSession().getPacketBufferSize());
// Check that we were able to wrap multiple times.
assertTrue(count >= 2);
encClientToServer.flip(); encClientToServer.flip();
result = server.unwrap(encClientToServer, plainServerOut); result = server.unwrap(encClientToServer, plainServerOut);
assertEquals(SSLEngineResult.Status.OK, result.getStatus()); assertEquals(SSLEngineResult.Status.OK, result.getStatus());
assertTrue(result.bytesConsumed() > 0); assertTrue(result.bytesConsumed() > 0);
assertTrue(result.bytesProduced() > 0); assertTrue(result.bytesProduced() > 0);
assertTrue(encClientToServer.hasRemaining());
} finally { } finally {
cert.delete(); cert.delete();
cleanupClientSslEngine(client); cleanupClientSslEngine(client);
@ -2270,12 +2412,16 @@ public abstract class SSLEngineTest {
.forClient() .forClient()
.trustManager(cert.cert()) .trustManager(cert.cert())
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
serverSslCtx = SslContextBuilder serverSslCtx = SslContextBuilder
.forServer(cert.certificate(), cert.privateKey()) .forServer(cert.certificate(), cert.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
@ -2341,12 +2487,16 @@ public abstract class SSLEngineTest {
.forClient() .forClient()
.trustManager(cert.cert()) .trustManager(cert.cert())
.sslProvider(sslClientProvider()) .sslProvider(sslClientProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
serverSslCtx = SslContextBuilder serverSslCtx = SslContextBuilder
.forServer(cert.certificate(), cert.privateKey()) .forServer(cert.certificate(), cert.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
@ -2393,6 +2543,8 @@ public abstract class SSLEngineTest {
SslContext ctx = SslContextBuilder SslContext ctx = SslContextBuilder
.forServer(cert.certificate(), cert.privateKey()) .forServer(cert.certificate(), cert.privateKey())
.sslProvider(sslServerProvider()) .sslProvider(sslServerProvider())
.protocols(protocols())
.ciphers(ciphers())
.build(); .build();
SSLEngine server = wrapEngine(ctx.newEngine(UnpooledByteBufAllocator.DEFAULT)); SSLEngine server = wrapEngine(ctx.newEngine(UnpooledByteBufAllocator.DEFAULT));
@ -2495,4 +2647,12 @@ public abstract class SSLEngineTest {
protected SSLEngine wrapEngine(SSLEngine engine) { protected SSLEngine wrapEngine(SSLEngine engine) {
return engine; return engine;
} }
protected List<String> ciphers() {
return Collections.singletonList(protocolCipherCombo.cipher);
}
protected String[] protocols() {
return new String[] { protocolCipherCombo.protocol };
}
} }

View File

@ -15,18 +15,19 @@
*/ */
package io.netty.handler.ssl; package io.netty.handler.ssl;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import io.netty.buffer.UnpooledByteBufAllocator; import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.handler.ssl.util.SelfSignedCertificate; import io.netty.handler.ssl.util.SelfSignedCertificate;
import org.junit.Assume; import org.junit.Assume;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
import java.util.Collections; import java.util.Collections;
import static org.junit.Assert.*;
public class SslContextBuilderTest { public class SslContextBuilderTest {
@Test @Test
@ -79,10 +80,19 @@ public class SslContextBuilderTest {
testInvalidCipher(SslProvider.JDK); testInvalidCipher(SslProvider.JDK);
} }
@Test(expected = SSLException.class) @Test
public void testInvalidCipherOpenSSL() throws Exception { public void testInvalidCipherOpenSSL() throws Exception {
Assume.assumeTrue(OpenSsl.isAvailable()); Assume.assumeTrue(OpenSsl.isAvailable());
testInvalidCipher(SslProvider.OPENSSL); try {
// This may fail or not depending on the OpenSSL version used
// See https://github.com/openssl/openssl/issues/7196
testInvalidCipher(SslProvider.OPENSSL);
if (!OpenSsl.versionString().contains("1.1.1")) {
fail();
}
} catch (SSLException expected) {
// ok
}
} }
private static void testInvalidCipher(SslProvider provider) throws Exception { private static void testInvalidCipher(SslProvider provider) throws Exception {

View File

@ -124,9 +124,10 @@ public class SslErrorTest {
Assume.assumeTrue(OpenSsl.isAvailable()); Assume.assumeTrue(OpenSsl.isAvailable());
SelfSignedCertificate ssc = new SelfSignedCertificate(); SelfSignedCertificate ssc = new SelfSignedCertificate();
final SslContext sslServerCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) final SslContext sslServerCtx = OpenSslTestUtils.configureProtocolForMutualAuth(
.sslProvider(serverProvider) SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
.trustManager(new SimpleTrustManagerFactory() { .sslProvider(serverProvider)
.trustManager(new SimpleTrustManagerFactory() {
@Override @Override
protected void engineInit(KeyStore keyStore) { } protected void engineInit(KeyStore keyStore) { }
@Override @Override
@ -154,13 +155,13 @@ public class SslErrorTest {
} }
} }; } };
} }
}).clientAuth(ClientAuth.REQUIRE).build(); }).clientAuth(ClientAuth.REQUIRE), clientProvider, serverProvider).build();
final SslContext sslClientCtx = SslContextBuilder.forClient() final SslContext sslClientCtx = OpenSslTestUtils.configureProtocolForMutualAuth(SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE) .trustManager(InsecureTrustManagerFactory.INSTANCE)
.keyManager(new File(getClass().getResource("test.crt").getFile()), .keyManager(new File(getClass().getResource("test.crt").getFile()),
new File(getClass().getResource("test_unencrypted.pem").getFile())) new File(getClass().getResource("test_unencrypted.pem").getFile()))
.sslProvider(clientProvider).build(); .sslProvider(clientProvider), clientProvider, serverProvider).build();
Channel serverChannel = null; Channel serverChannel = null;
Channel clientChannel = null; Channel clientChannel = null;

View File

@ -28,6 +28,7 @@ import java.security.NoSuchAlgorithmException;
import static io.netty.handler.ssl.SslUtils.getEncryptedPacketLength; import static io.netty.handler.ssl.SslUtils.getEncryptedPacketLength;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
public class SslUtilsTest { public class SslUtilsTest {
@ -63,4 +64,15 @@ public class SslUtilsTest {
engine.beginHandshake(); engine.beginHandshake();
return engine; return engine;
} }
@Test
public void testIsTLSv13Cipher() {
assertTrue(SslUtils.isTLSv13Cipher("TLS_AES_128_GCM_SHA256"));
assertTrue(SslUtils.isTLSv13Cipher("TLS_AES_256_GCM_SHA384"));
assertTrue(SslUtils.isTLSv13Cipher("TLS_CHACHA20_POLY1305_SHA256"));
assertTrue(SslUtils.isTLSv13Cipher("TLS_AES_128_CCM_SHA256"));
assertTrue(SslUtils.isTLSv13Cipher("TLS_AES_128_CCM_8_SHA256"));
assertFalse(SslUtils.isTLSv13Cipher("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256"));
}
} }

View File

@ -241,7 +241,7 @@
<!-- Fedora-"like" systems. This is currently only used for the netty-tcnative dependency --> <!-- Fedora-"like" systems. This is currently only used for the netty-tcnative dependency -->
<os.detection.classifierWithLikes>fedora</os.detection.classifierWithLikes> <os.detection.classifierWithLikes>fedora</os.detection.classifierWithLikes>
<tcnative.artifactId>netty-tcnative</tcnative.artifactId> <tcnative.artifactId>netty-tcnative</tcnative.artifactId>
<tcnative.version>2.0.17.Final</tcnative.version> <tcnative.version>2.0.18.Final</tcnative.version>
<tcnative.classifier>${os.detected.classifier}</tcnative.classifier> <tcnative.classifier>${os.detected.classifier}</tcnative.classifier>
<conscrypt.groupId>org.conscrypt</conscrypt.groupId> <conscrypt.groupId>org.conscrypt</conscrypt.groupId>
<conscrypt.artifactId>conscrypt-openjdk-uber</conscrypt.artifactId> <conscrypt.artifactId>conscrypt-openjdk-uber</conscrypt.artifactId>

View File

@ -138,7 +138,8 @@ public class SocketSslClientRenegotiateTest extends AbstractSocketTest {
public void initChannel(Channel sch) throws Exception { public void initChannel(Channel sch) throws Exception {
serverChannel = sch; serverChannel = sch;
serverSslHandler = serverCtx.newHandler(sch.alloc()); serverSslHandler = serverCtx.newHandler(sch.alloc());
// As we test renegotiation we should use a protocol that support it.
serverSslHandler.engine().setEnabledProtocols(new String[] { "TLSv1.2" });
sch.pipeline().addLast("ssl", serverSslHandler); sch.pipeline().addLast("ssl", serverSslHandler);
sch.pipeline().addLast("handler", serverHandler); sch.pipeline().addLast("handler", serverHandler);
} }
@ -150,7 +151,8 @@ public class SocketSslClientRenegotiateTest extends AbstractSocketTest {
public void initChannel(Channel sch) throws Exception { public void initChannel(Channel sch) throws Exception {
clientChannel = sch; clientChannel = sch;
clientSslHandler = clientCtx.newHandler(sch.alloc()); clientSslHandler = clientCtx.newHandler(sch.alloc());
// As we test renegotiation we should use a protocol that support it.
clientSslHandler.engine().setEnabledProtocols(new String[] { "TLSv1.2" });
sch.pipeline().addLast("ssl", clientSslHandler); sch.pipeline().addLast("ssl", clientSslHandler);
sch.pipeline().addLast("handler", clientHandler); sch.pipeline().addLast("handler", clientHandler);
} }

View File

@ -123,17 +123,33 @@ public class SocketSslEchoTest extends AbstractSocketTest {
"autoRead = {5}, useChunkedWriteHandler = {6}, useCompositeByteBuf = {7}") "autoRead = {5}, useChunkedWriteHandler = {6}, useCompositeByteBuf = {7}")
public static Collection<Object[]> data() throws Exception { public static Collection<Object[]> data() throws Exception {
List<SslContext> serverContexts = new ArrayList<SslContext>(); List<SslContext> serverContexts = new ArrayList<SslContext>();
serverContexts.add(SslContextBuilder.forServer(CERT_FILE, KEY_FILE).sslProvider(SslProvider.JDK).build()); serverContexts.add(SslContextBuilder.forServer(CERT_FILE, KEY_FILE)
.sslProvider(SslProvider.JDK)
// As we test renegotiation we should use a protocol that support it.
.protocols("TLSv1.2")
.build());
List<SslContext> clientContexts = new ArrayList<SslContext>(); List<SslContext> clientContexts = new ArrayList<SslContext>();
clientContexts.add(SslContextBuilder.forClient().sslProvider(SslProvider.JDK).trustManager(CERT_FILE).build()); clientContexts.add(SslContextBuilder.forClient()
.sslProvider(SslProvider.JDK)
.trustManager(CERT_FILE)
// As we test renegotiation we should use a protocol that support it.
.protocols("TLSv1.2")
.build());
boolean hasOpenSsl = OpenSsl.isAvailable(); boolean hasOpenSsl = OpenSsl.isAvailable();
if (hasOpenSsl) { if (hasOpenSsl) {
serverContexts.add(SslContextBuilder.forServer(CERT_FILE, KEY_FILE) serverContexts.add(SslContextBuilder.forServer(CERT_FILE, KEY_FILE)
.sslProvider(SslProvider.OPENSSL).build()); .sslProvider(SslProvider.OPENSSL)
clientContexts.add(SslContextBuilder.forClient().sslProvider(SslProvider.OPENSSL) // As we test renegotiation we should use a protocol that support it.
.trustManager(CERT_FILE).build()); .protocols("TLSv1.2")
.build());
clientContexts.add(SslContextBuilder.forClient()
.sslProvider(SslProvider.OPENSSL)
.trustManager(CERT_FILE)
// As we test renegotiation we should use a protocol that support it.
.protocols("TLSv1.2")
.build());
} else { } else {
logger.warn("OpenSSL is unavailable and thus will not be tested.", OpenSsl.unavailabilityCause()); logger.warn("OpenSSL is unavailable and thus will not be tested.", OpenSsl.unavailabilityCause());
} }

View File

@ -98,7 +98,7 @@ public class SocketSslSessionReuseTest extends AbstractSocketTest {
public void testSslSessionReuse(ServerBootstrap sb, Bootstrap cb) throws Throwable { public void testSslSessionReuse(ServerBootstrap sb, Bootstrap cb) throws Throwable {
final ReadAndDiscardHandler sh = new ReadAndDiscardHandler(true, true); final ReadAndDiscardHandler sh = new ReadAndDiscardHandler(true, true);
final ReadAndDiscardHandler ch = new ReadAndDiscardHandler(false, true); final ReadAndDiscardHandler ch = new ReadAndDiscardHandler(false, true);
final String[] protocols = new String[]{ "TLSv1", "TLSv1.1", "TLSv1.2" }; final String[] protocols = { "TLSv1", "TLSv1.1", "TLSv1.2" };
sb.childHandler(new ChannelInitializer<SocketChannel>() { sb.childHandler(new ChannelInitializer<SocketChannel>() {
@Override @Override