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:
parent
9eebe7ed74
commit
0ddc62cec0
@ -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.
|
||||
*
|
||||
@ -423,5 +396,47 @@ final class CipherSuiteConverter {
|
||||
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() { }
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import io.netty.util.internal.logging.InternalLogger;
|
||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
|
||||
@ -26,6 +27,7 @@ import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.KeyException;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Provider;
|
||||
import java.security.Security;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.CertificateException;
|
||||
@ -34,6 +36,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@ -58,11 +61,13 @@ public class JdkSslContext extends SslContext {
|
||||
static final String PROTOCOL = "TLS";
|
||||
private static final String[] DEFAULT_PROTOCOLS;
|
||||
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_NON_TLSV13;
|
||||
private static final Provider DEFAULT_PROVIDER;
|
||||
|
||||
static {
|
||||
SSLContext context;
|
||||
int i;
|
||||
try {
|
||||
context = SSLContext.getInstance(PROTOCOL);
|
||||
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);
|
||||
}
|
||||
|
||||
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.
|
||||
final String[] supportedProtocols = engine.getSupportedProtocols();
|
||||
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]);
|
||||
}
|
||||
List<String> protocols = new ArrayList<String>();
|
||||
addIfSupported(
|
||||
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()) {
|
||||
DEFAULT_PROTOCOLS = protocols.toArray(new String[0]);
|
||||
} else {
|
||||
DEFAULT_PROTOCOLS = engine.getEnabledProtocols();
|
||||
return protocols.toArray(new String[0]);
|
||||
}
|
||||
return engine.getEnabledProtocols();
|
||||
}
|
||||
|
||||
private static Set<String> supportedCiphers(SSLEngine engine) {
|
||||
// Choose the sensible default list of cipher suites.
|
||||
final String[] supportedCiphers = engine.getSupportedCipherSuites();
|
||||
SUPPORTED_CIPHERS = new HashSet<String>(supportedCiphers.length);
|
||||
for (i = 0; i < supportedCiphers.length; ++i) {
|
||||
Set<String> supportedCiphersSet = new LinkedHashSet<String>(supportedCiphers.length);
|
||||
for (int i = 0; i < supportedCiphers.length; ++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_"
|
||||
// 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.
|
||||
@ -108,21 +136,29 @@ public class JdkSslContext extends SslContext {
|
||||
final String tlsPrefixedCipherName = "TLS_" + supportedCipher.substring("SSL_".length());
|
||||
try {
|
||||
engine.setEnabledCipherSuites(new String[]{tlsPrefixedCipherName});
|
||||
SUPPORTED_CIPHERS.add(tlsPrefixedCipherName);
|
||||
supportedCiphersSet.add(tlsPrefixedCipherName);
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
// The cipher is not supported ... move on to the next cipher.
|
||||
}
|
||||
}
|
||||
}
|
||||
List<String> ciphers = new ArrayList<String>();
|
||||
addIfSupported(SUPPORTED_CIPHERS, ciphers, DEFAULT_CIPHER_SUITES);
|
||||
useFallbackCiphersIfDefaultIsEmpty(ciphers, engine.getEnabledCipherSuites());
|
||||
DEFAULT_CIPHERS = Collections.unmodifiableList(ciphers);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Default protocols (JDK): {} ", Arrays.asList(DEFAULT_PROTOCOLS));
|
||||
logger.debug("Default cipher suites (JDK): {}", DEFAULT_CIPHERS);
|
||||
return supportedCiphersSet;
|
||||
}
|
||||
|
||||
private static List<String> defaultCiphers(SSLEngine engine, Set<String> supportedCiphers) {
|
||||
List<String> ciphers = new ArrayList<String>();
|
||||
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;
|
||||
@ -205,11 +241,49 @@ public class JdkSslContext extends SslContext {
|
||||
super(startTls);
|
||||
this.apn = checkNotNull(apn, "apn");
|
||||
this.clientAuth = checkNotNull(clientAuth, "clientAuth");
|
||||
cipherSuites = checkNotNull(cipherFilter, "cipherFilter").filterCipherSuites(
|
||||
ciphers, DEFAULT_CIPHERS, SUPPORTED_CIPHERS);
|
||||
this.protocols = protocols == null ? DEFAULT_PROTOCOLS : protocols;
|
||||
unmodifiableCipherSuites = Collections.unmodifiableList(Arrays.asList(cipherSuites));
|
||||
this.sslContext = checkNotNull(sslContext, "sslContext");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@ import io.netty.util.internal.SystemPropertyUtil;
|
||||
import io.netty.util.internal.logging.InternalLogger;
|
||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
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_1;
|
||||
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
|
||||
@ -66,6 +68,12 @@ public final class OpenSsl {
|
||||
private static final boolean SUPPORTS_HOSTNAME_VALIDATION;
|
||||
private static final boolean USE_KEYMANAGER_FACTORY;
|
||||
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;
|
||||
|
||||
@ -139,17 +147,30 @@ public final class OpenSsl {
|
||||
boolean supportsKeyManagerFactory = false;
|
||||
boolean useKeyManagerFactory = false;
|
||||
boolean supportsHostNameValidation = false;
|
||||
boolean tlsv13Supported = false;
|
||||
|
||||
try {
|
||||
final long sslCtx = SSLContext.make(SSL.SSL_PROTOCOL_ALL, SSL.SSL_MODE_SERVER);
|
||||
long certBio = 0;
|
||||
SelfSignedCertificate cert = null;
|
||||
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);
|
||||
try {
|
||||
for (String c: SSL.getCiphers(ssl)) {
|
||||
// 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;
|
||||
}
|
||||
availableOpenSslCipherSuites.add(c);
|
||||
@ -200,8 +221,13 @@ public final class OpenSsl {
|
||||
AVAILABLE_OPENSSL_CIPHER_SUITES.size() * 2);
|
||||
for (String cipher: AVAILABLE_OPENSSL_CIPHER_SUITES) {
|
||||
// Included converted but also openssl cipher name
|
||||
if (!SslUtils.isTLSv13Cipher(cipher)) {
|
||||
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);
|
||||
@ -239,6 +265,18 @@ public final class OpenSsl {
|
||||
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);
|
||||
SUPPORTS_OCSP = doesSupportOcsp();
|
||||
|
||||
@ -256,6 +294,7 @@ public final class OpenSsl {
|
||||
USE_KEYMANAGER_FACTORY = false;
|
||||
SUPPORTED_PROTOCOLS_SET = Collections.emptySet();
|
||||
SUPPORTS_OCSP = false;
|
||||
TLSV13_SUPPORTED = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -450,4 +489,8 @@ public final class OpenSsl {
|
||||
ReferenceCountUtil.safeRelease(counted);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isTlsv13Supported() {
|
||||
return TLSV13_SUPPORTED;
|
||||
}
|
||||
}
|
||||
|
@ -24,8 +24,11 @@ import io.netty.internal.tcnative.SSLContext;
|
||||
import java.security.KeyStore;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
@ -47,6 +50,12 @@ import javax.security.auth.x500.X500Principal;
|
||||
public final class ReferenceCountedOpenSslClientContext extends ReferenceCountedOpenSslContext {
|
||||
private static final InternalLogger logger =
|
||||
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;
|
||||
|
||||
ReferenceCountedOpenSslClientContext(X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory,
|
||||
@ -277,7 +286,8 @@ public final class ReferenceCountedOpenSslClientContext extends ReferenceCounted
|
||||
*/
|
||||
private static Set<String> supportedClientKeyTypes(byte[] clientCertificateTypes) {
|
||||
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);
|
||||
for (byte keyTypeCode : clientCertificateTypes) {
|
||||
|
@ -225,16 +225,53 @@ public abstract class ReferenceCountedOpenSslContext extends SslContext implemen
|
||||
boolean success = false;
|
||||
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;
|
||||
ctx = SSLContext.make(opts, mode);
|
||||
if (OpenSsl.isTlsv13Supported()) {
|
||||
protocolOpts |= SSL.SSL_PROTOCOL_TLSV1_3;
|
||||
}
|
||||
ctx = SSLContext.make(protocolOpts, mode);
|
||||
} catch (Exception e) {
|
||||
throw new SSLException("failed to create an SSL_CTX", e);
|
||||
}
|
||||
|
||||
SSLContext.setOptions(ctx, SSLContext.getOptions(ctx) |
|
||||
boolean tlsv13Supported = OpenSsl.isTlsv13Supported();
|
||||
StringBuilder cipherBuilder = new StringBuilder();
|
||||
StringBuilder cipherTLSv13Builder = new StringBuilder();
|
||||
|
||||
/* List the ciphers that are permitted to negotiate. */
|
||||
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);
|
||||
|
||||
// Set non TLSv1.3 ciphers.
|
||||
SSLContext.setCipherSuite(ctx, cipherBuilder.toString(), false);
|
||||
if (tlsv13Supported) {
|
||||
// Set TLSv1.3 ciphers.
|
||||
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.
|
||||
@ -244,7 +281,15 @@ public abstract class ReferenceCountedOpenSslContext extends SslContext implemen
|
||||
// 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);
|
||||
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
|
||||
// calling OpenSSLEngine.wrap(...).
|
||||
@ -255,15 +300,6 @@ public abstract class ReferenceCountedOpenSslContext extends SslContext implemen
|
||||
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();
|
||||
/* Set next protocols for next protocol negotiation extension, if specified */
|
||||
if (!nextProtoList.isEmpty()) {
|
||||
|
@ -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_1;
|
||||
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.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_JAVAX_X509_CERTIFICATES;
|
||||
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_1 = 3;
|
||||
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 = {
|
||||
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_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>.
|
||||
@ -124,11 +125,11 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
||||
/**
|
||||
* 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!
|
||||
*/
|
||||
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 =
|
||||
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
|
||||
// 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
|
||||
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
|
||||
// that the renegotiation failed.
|
||||
shutdown();
|
||||
@ -1379,15 +1383,18 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
||||
if (enabled == null) {
|
||||
return EmptyArrays.EMPTY_STRINGS;
|
||||
} else {
|
||||
List<String> enabledList = new ArrayList<String>();
|
||||
synchronized (this) {
|
||||
for (int i = 0; i < enabled.length; i++) {
|
||||
String mapped = toJavaCipherSuite(enabled[i]);
|
||||
if (mapped != null) {
|
||||
enabled[i] = mapped;
|
||||
final String cipher = mapped == null ? 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");
|
||||
|
||||
final StringBuilder buf = new StringBuilder();
|
||||
for (String c: cipherSuites) {
|
||||
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);
|
||||
final StringBuilder bufTLSv13 = new StringBuilder();
|
||||
|
||||
CipherSuiteConverter.convertToCipherStrings(Arrays.asList(cipherSuites), buf, bufTLSv13);
|
||||
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) {
|
||||
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 {
|
||||
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) {
|
||||
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)) {
|
||||
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)) {
|
||||
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) {
|
||||
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) {
|
||||
if (!isDestroyed()) {
|
||||
// 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.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;
|
||||
for (int i = 0; i < minProtocolIndex; ++i) {
|
||||
|
@ -22,9 +22,14 @@ import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.base64.Base64;
|
||||
import io.netty.handler.codec.base64.Base64Dialect;
|
||||
import io.netty.util.NetUtil;
|
||||
import io.netty.util.internal.EmptyArrays;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@ -36,7 +41,11 @@ import static java.util.Arrays.asList;
|
||||
* Constants for SSL packets.
|
||||
*/
|
||||
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
|
||||
static final String PROTOCOL_SSL_V2_HELLO = "SSLv2Hello";
|
||||
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_1 = "TLSv1.1";
|
||||
static final String PROTOCOL_TLS_V1_2 = "TLSv1.2";
|
||||
static final String PROTOCOL_TLS_V1_3 = "TLSv1.3";
|
||||
|
||||
/**
|
||||
* change cipher spec
|
||||
@ -85,20 +95,36 @@ final class SslUtils {
|
||||
*/
|
||||
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.
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
||||
defaultCiphers.add("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384");
|
||||
defaultCiphers.add("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256");
|
||||
defaultCiphers.add("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
|
||||
defaultCiphers.add("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");
|
||||
// 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.
|
||||
"TLS_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA",
|
||||
defaultCiphers.add("TLS_RSA_WITH_AES_128_GCM_SHA256");
|
||||
defaultCiphers.add("TLS_RSA_WITH_AES_128_CBC_SHA");
|
||||
// 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}.
|
||||
@ -361,6 +387,14 @@ final class SslUtils {
|
||||
!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() {
|
||||
}
|
||||
}
|
||||
|
@ -125,12 +125,16 @@ public class CipherSuiteCanaryTest {
|
||||
final SslContext sslServerContext = SslContextBuilder.forServer(CERT.certificate(), CERT.privateKey())
|
||||
.sslProvider(serverSslProvider)
|
||||
.ciphers(ciphers)
|
||||
// As this is not a TLSv1.3 cipher we should ensure we talk something else.
|
||||
.protocols(SslUtils.PROTOCOL_TLS_V1_2)
|
||||
.build();
|
||||
|
||||
try {
|
||||
final SslContext sslClientContext = SslContextBuilder.forClient()
|
||||
.sslProvider(clientSslProvider)
|
||||
.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)
|
||||
.build();
|
||||
|
||||
|
@ -22,8 +22,7 @@ import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.sameInstance;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class CipherSuiteConverterTest {
|
||||
|
||||
|
@ -31,17 +31,17 @@ import static org.junit.Assume.assumeTrue;
|
||||
@RunWith(Parameterized.class)
|
||||
public class ConscryptJdkSslEngineInteropTest extends SSLEngineTest {
|
||||
|
||||
@Parameterized.Parameters(name = "{index}: bufferType = {0}")
|
||||
public static Collection<Object> data() {
|
||||
List<Object> params = new ArrayList<Object>();
|
||||
@Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}")
|
||||
public static Collection<Object[]> data() {
|
||||
List<Object[]> params = new ArrayList<Object[]>();
|
||||
for (BufferType type: BufferType.values()) {
|
||||
params.add(type);
|
||||
params.add(new Object[] { type, ProtocolCipherCombo.tlsv12()});
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
public ConscryptJdkSslEngineInteropTest(BufferType type) {
|
||||
super(type);
|
||||
public ConscryptJdkSslEngineInteropTest(BufferType type, ProtocolCipherCombo combo) {
|
||||
super(type, combo);
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
|
@ -30,17 +30,17 @@ import static org.junit.Assume.assumeTrue;
|
||||
@RunWith(Parameterized.class)
|
||||
public class ConscryptSslEngineTest extends SSLEngineTest {
|
||||
|
||||
@Parameterized.Parameters(name = "{index}: bufferType = {0}")
|
||||
public static Collection<Object> data() {
|
||||
List<Object> params = new ArrayList<Object>();
|
||||
@Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}")
|
||||
public static Collection<Object[]> data() {
|
||||
List<Object[]> params = new ArrayList<Object[]>();
|
||||
for (BufferType type: BufferType.values()) {
|
||||
params.add(type);
|
||||
params.add(new Object[] { type, ProtocolCipherCombo.tlsv12()});
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
public ConscryptSslEngineTest(BufferType type) {
|
||||
super(type);
|
||||
public ConscryptSslEngineTest(BufferType type, ProtocolCipherCombo combo) {
|
||||
super(type, combo);
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
|
@ -16,6 +16,7 @@
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import java.security.Provider;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
@ -31,17 +32,17 @@ import static org.junit.Assume.assumeTrue;
|
||||
@RunWith(Parameterized.class)
|
||||
public class JdkConscryptSslEngineInteropTest extends SSLEngineTest {
|
||||
|
||||
@Parameterized.Parameters(name = "{index}: bufferType = {0}")
|
||||
public static Collection<Object> data() {
|
||||
List<Object> params = new ArrayList<Object>();
|
||||
@Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}")
|
||||
public static Collection<Object[]> data() {
|
||||
List<Object[]> params = new ArrayList<Object[]>();
|
||||
for (BufferType type: BufferType.values()) {
|
||||
params.add(type);
|
||||
params.add(new Object[] { type, ProtocolCipherCombo.tlsv12()});
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
public JdkConscryptSslEngineInteropTest(BufferType type) {
|
||||
super(type);
|
||||
public JdkConscryptSslEngineInteropTest(BufferType type, ProtocolCipherCombo combo) {
|
||||
super(type, combo);
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@ -32,17 +33,21 @@ import static org.junit.Assume.assumeTrue;
|
||||
@RunWith(Parameterized.class)
|
||||
public class JdkOpenSslEngineInteroptTest extends SSLEngineTest {
|
||||
|
||||
@Parameterized.Parameters(name = "{index}: bufferType = {0}")
|
||||
public static Collection<Object> data() {
|
||||
List<Object> params = new ArrayList<Object>();
|
||||
@Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}")
|
||||
public static Collection<Object[]> data() {
|
||||
List<Object[]> params = new ArrayList<Object[]>();
|
||||
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;
|
||||
}
|
||||
|
||||
public JdkOpenSslEngineInteroptTest(BufferType type) {
|
||||
super(type);
|
||||
public JdkOpenSslEngineInteroptTest(BufferType type, ProtocolCipherCombo protocolCipherCombo) {
|
||||
super(type, protocolCipherCombo);
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
|
@ -26,6 +26,7 @@ import java.security.Provider;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import io.netty.util.internal.EmptyArrays;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
import org.junit.Ignore;
|
||||
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 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() {
|
||||
List<Object[]> params = new ArrayList<Object[]>();
|
||||
for (ProviderType providerType : ProviderType.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;
|
||||
@ -156,8 +160,8 @@ public class JdkSslEngineTest extends SSLEngineTest {
|
||||
|
||||
private Provider provider;
|
||||
|
||||
public JdkSslEngineTest(ProviderType providerType, BufferType bufferType) {
|
||||
super(bufferType);
|
||||
public JdkSslEngineTest(ProviderType providerType, BufferType bufferType, ProtocolCipherCombo protocolCipherCombo) {
|
||||
super(bufferType, protocolCipherCombo);
|
||||
this.providerType = providerType;
|
||||
}
|
||||
|
||||
@ -235,9 +239,11 @@ public class JdkSslEngineTest extends SSLEngineTest {
|
||||
InsecureTrustManagerFactory.INSTANCE, null,
|
||||
IdentityCipherSuiteFilter.INSTANCE, clientApn, 0, 0);
|
||||
|
||||
setupHandlers(serverSslCtx, clientSslCtx);
|
||||
setupHandlers(new TestDelegatingSslContext(serverSslCtx), new TestDelegatingSslContext(clientSslCtx));
|
||||
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) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -67,17 +67,21 @@ public class OpenSslEngineTest extends SSLEngineTest {
|
||||
private static final String PREFERRED_APPLICATION_LEVEL_PROTOCOL = "my-protocol-http2";
|
||||
private static final String FALLBACK_APPLICATION_LEVEL_PROTOCOL = "my-protocol-http1_1";
|
||||
|
||||
@Parameterized.Parameters(name = "{index}: bufferType = {0}")
|
||||
public static Collection<Object> data() {
|
||||
List<Object> params = new ArrayList<Object>();
|
||||
@Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}")
|
||||
public static Collection<Object[]> data() {
|
||||
List<Object[]> params = new ArrayList<Object[]>();
|
||||
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;
|
||||
}
|
||||
|
||||
public OpenSslEngineTest(BufferType type) {
|
||||
super(type);
|
||||
public OpenSslEngineTest(BufferType type, ProtocolCipherCombo cipherCombo) {
|
||||
super(type, cipherCombo);
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
@ -208,10 +212,14 @@ public class OpenSslEngineTest extends SSLEngineTest {
|
||||
clientSslCtx = SslContextBuilder.forClient()
|
||||
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
||||
.sslProvider(sslClientProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||
serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
||||
.sslProvider(sslServerProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine clientEngine = null;
|
||||
SSLEngine serverEngine = null;
|
||||
@ -242,10 +250,14 @@ public class OpenSslEngineTest extends SSLEngineTest {
|
||||
clientSslCtx = SslContextBuilder.forClient()
|
||||
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
||||
.sslProvider(sslClientProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||
serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
||||
.sslProvider(sslServerProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine clientEngine = null;
|
||||
SSLEngine serverEngine = null;
|
||||
@ -293,10 +305,14 @@ public class OpenSslEngineTest extends SSLEngineTest {
|
||||
clientSslCtx = SslContextBuilder.forClient()
|
||||
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
||||
.sslProvider(sslClientProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||
serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
||||
.sslProvider(sslServerProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine clientEngine = null;
|
||||
SSLEngine serverEngine = null;
|
||||
@ -329,10 +345,14 @@ public class OpenSslEngineTest extends SSLEngineTest {
|
||||
clientSslCtx = SslContextBuilder.forClient()
|
||||
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
||||
.sslProvider(sslClientProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||
serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
||||
.sslProvider(sslServerProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine clientEngine = null;
|
||||
SSLEngine serverEngine = null;
|
||||
@ -376,6 +396,8 @@ public class OpenSslEngineTest extends SSLEngineTest {
|
||||
clientSslCtx = SslContextBuilder.forClient()
|
||||
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
||||
.sslProvider(sslClientProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine clientEngine = null;
|
||||
try {
|
||||
@ -392,6 +414,8 @@ public class OpenSslEngineTest extends SSLEngineTest {
|
||||
clientSslCtx = SslContextBuilder.forClient()
|
||||
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
||||
.sslProvider(sslClientProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine clientEngine = null;
|
||||
try {
|
||||
@ -416,11 +440,15 @@ public class OpenSslEngineTest extends SSLEngineTest {
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||
serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
||||
.sslProvider(sslServerProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
|
||||
clientSslCtx = SslContextBuilder.forClient()
|
||||
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
||||
.sslProvider(sslClientProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine clientEngine = null;
|
||||
SSLEngine serverEngine = null;
|
||||
@ -613,12 +641,16 @@ public class OpenSslEngineTest extends SSLEngineTest {
|
||||
.forClient()
|
||||
.trustManager(cert.cert())
|
||||
.sslProvider(sslClientProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine());
|
||||
|
||||
serverSslCtx = SslContextBuilder
|
||||
.forServer(cert.certificate(), cert.privateKey())
|
||||
.sslProvider(sslServerProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine server = wrapEngine(serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine());
|
||||
|
||||
@ -690,12 +722,16 @@ public class OpenSslEngineTest extends SSLEngineTest {
|
||||
.forClient()
|
||||
.trustManager(cert.cert())
|
||||
.sslProvider(sslClientProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine());
|
||||
|
||||
serverSslCtx = SslContextBuilder
|
||||
.forServer(cert.certificate(), cert.privateKey())
|
||||
.sslProvider(sslServerProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine server = wrapEngine(serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine());
|
||||
|
||||
@ -774,12 +810,16 @@ public class OpenSslEngineTest extends SSLEngineTest {
|
||||
.forClient()
|
||||
.trustManager(cert.cert())
|
||||
.sslProvider(sslClientProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine());
|
||||
|
||||
serverSslCtx = SslContextBuilder
|
||||
.forServer(cert.certificate(), cert.privateKey())
|
||||
.sslProvider(sslServerProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine server = wrapEngine(serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine());
|
||||
|
||||
@ -849,12 +889,16 @@ public class OpenSslEngineTest extends SSLEngineTest {
|
||||
.forClient()
|
||||
.trustManager(cert.cert())
|
||||
.sslProvider(sslClientProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine());
|
||||
|
||||
serverSslCtx = SslContextBuilder
|
||||
.forServer(cert.certificate(), cert.privateKey())
|
||||
.sslProvider(sslServerProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine server = wrapEngine(serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine());
|
||||
|
||||
@ -983,6 +1027,8 @@ public class OpenSslEngineTest extends SSLEngineTest {
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||
serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
||||
.sslProvider(sslServerProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
|
||||
SSLEngine engine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
|
||||
@ -1003,6 +1049,8 @@ public class OpenSslEngineTest extends SSLEngineTest {
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||
serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
||||
.sslProvider(sslServerProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
|
||||
SSLEngine engine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
|
||||
@ -1023,6 +1071,8 @@ public class OpenSslEngineTest extends SSLEngineTest {
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||
serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
||||
.sslProvider(sslServerProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
|
||||
SSLEngine engine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
@ -34,17 +35,21 @@ import static org.junit.Assume.assumeTrue;
|
||||
@RunWith(Parameterized.class)
|
||||
public class OpenSslJdkSslEngineInteroptTest extends SSLEngineTest {
|
||||
|
||||
@Parameterized.Parameters(name = "{index}: bufferType = {0}")
|
||||
public static Collection<Object> data() {
|
||||
List<Object> params = new ArrayList<Object>();
|
||||
@Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}")
|
||||
public static Collection<Object[]> data() {
|
||||
List<Object[]> params = new ArrayList<Object[]>();
|
||||
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;
|
||||
}
|
||||
|
||||
public OpenSslJdkSslEngineInteroptTest(BufferType type) {
|
||||
super(type);
|
||||
public OpenSslJdkSslEngineInteroptTest(BufferType type, ProtocolCipherCombo combo) {
|
||||
super(type, combo);
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
|
@ -15,6 +15,12 @@
|
||||
*/
|
||||
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;
|
||||
|
||||
final class OpenSslTestUtils {
|
||||
@ -28,4 +34,17 @@ final class OpenSslTestUtils {
|
||||
static boolean isBoringSSL() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -382,11 +382,20 @@ public class ParameterizedSslHandlerTest {
|
||||
|
||||
final SslContext sslServerCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
||||
.sslProvider(serverProvider)
|
||||
// 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()
|
||||
.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();
|
||||
Channel sc = null;
|
||||
|
@ -23,8 +23,8 @@ import javax.net.ssl.SSLEngine;
|
||||
|
||||
public class ReferenceCountedOpenSslEngineTest extends OpenSslEngineTest {
|
||||
|
||||
public ReferenceCountedOpenSslEngineTest(BufferType type) {
|
||||
super(type);
|
||||
public ReferenceCountedOpenSslEngineTest(BufferType type, ProtocolCipherCombo combo) {
|
||||
super(type, combo);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -62,6 +62,8 @@ public class ReferenceCountedOpenSslEngineTest extends OpenSslEngineTest {
|
||||
clientSslCtx = SslContextBuilder.forClient()
|
||||
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
||||
.sslProvider(sslClientProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
|
||||
clientSslCtx.newEngine(null);
|
||||
|
@ -33,6 +33,7 @@ import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
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.SelfSignedCertificate;
|
||||
import io.netty.handler.ssl.util.SimpleTrustManagerFactory;
|
||||
@ -67,6 +68,7 @@ import java.security.Provider;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@ -79,6 +81,7 @@ import javax.net.ssl.ManagerFactoryParameters;
|
||||
import javax.net.ssl.SNIHostName;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLEngineResult;
|
||||
import javax.net.ssl.SSLEngineResult.Status;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import javax.net.ssl.SSLParameters;
|
||||
@ -89,14 +92,7 @@ import javax.net.ssl.TrustManagerFactorySpi;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import javax.security.cert.X509Certificate;
|
||||
|
||||
import static io.netty.handler.ssl.SslUtils.PROTOCOL_SSL_V2;
|
||||
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 io.netty.handler.ssl.SslUtils.*;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
@ -220,10 +216,42 @@ public abstract class SSLEngineTest {
|
||||
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.protocolCipherCombo = protocolCipherCombo;
|
||||
}
|
||||
|
||||
protected ByteBuffer allocateBuffer(int len) {
|
||||
@ -620,36 +648,46 @@ public abstract class SSLEngineTest {
|
||||
}
|
||||
|
||||
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) {
|
||||
}
|
||||
|
||||
private SslContextBuilder configureProtocolForMutualAuth(SslContextBuilder ctx) {
|
||||
return OpenSslTestUtils.configureProtocolForMutualAuth(ctx, sslClientProvider(), sslServerProvider());
|
||||
}
|
||||
|
||||
private void mySetupMutualAuth(KeyManagerFactory serverKMF, final File serverTrustManager,
|
||||
KeyManagerFactory clientKMF, File clientTrustManager,
|
||||
ClientAuth clientAuth, final boolean failureExpected,
|
||||
final boolean serverInitEngine)
|
||||
throws SSLException, InterruptedException {
|
||||
serverSslCtx = SslContextBuilder.forServer(serverKMF)
|
||||
serverSslCtx = configureProtocolForMutualAuth(
|
||||
SslContextBuilder.forServer(serverKMF)
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.sslProvider(sslServerProvider())
|
||||
.sslContextProvider(serverSslContextProvider())
|
||||
.trustManager(serverTrustManager)
|
||||
.clientAuth(clientAuth)
|
||||
.ciphers(null, IdentityCipherSuiteFilter.INSTANCE)
|
||||
.sessionCacheSize(0)
|
||||
.sessionTimeout(0)
|
||||
.build();
|
||||
.sessionTimeout(0)).build();
|
||||
|
||||
clientSslCtx = SslContextBuilder.forClient()
|
||||
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();
|
||||
.sessionTimeout(0)).build();
|
||||
|
||||
serverConnectedChannel = null;
|
||||
sb = new ServerBootstrap();
|
||||
cb = new Bootstrap();
|
||||
@ -711,10 +749,11 @@ public abstract class SSLEngineTest {
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
|
||||
if (evt == SslHandshakeCompletionEvent.SUCCESS) {
|
||||
if (failureExpected) {
|
||||
clientException = new IllegalStateException("handshake complete. expected failure");
|
||||
}
|
||||
// With TLS1.3 a mutal auth error will not be propagated as a handshake error most of the
|
||||
// time as the handshake needs NO extra roundtrip.
|
||||
if (!failureExpected) {
|
||||
clientLatch.countDown();
|
||||
}
|
||||
} else if (evt instanceof SslHandshakeCompletionEvent) {
|
||||
clientException = ((SslHandshakeCompletionEvent) evt).cause();
|
||||
clientLatch.countDown();
|
||||
@ -724,7 +763,7 @@ public abstract class SSLEngineTest {
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
if (cause.getCause() instanceof SSLHandshakeException) {
|
||||
if (cause.getCause() instanceof SSLException) {
|
||||
clientException = cause.getCause();
|
||||
clientLatch.countDown();
|
||||
} 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();
|
||||
|
||||
ChannelFuture ccf = cb.connect(new InetSocketAddress(NetUtil.LOCALHOST, port));
|
||||
@ -776,6 +815,8 @@ public abstract class SSLEngineTest {
|
||||
final String expectedHost = "localhost";
|
||||
serverSslCtx = SslContextBuilder.forServer(serverCrtFile, serverKeyFile, null)
|
||||
.sslProvider(sslServerProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.sslContextProvider(serverSslContextProvider())
|
||||
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
||||
.ciphers(null, IdentityCipherSuiteFilter.INSTANCE)
|
||||
@ -785,12 +826,15 @@ public abstract class SSLEngineTest {
|
||||
|
||||
clientSslCtx = SslContextBuilder.forClient()
|
||||
.sslProvider(sslClientProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.sslContextProvider(clientSslContextProvider())
|
||||
.trustManager(clientTrustCrtFile)
|
||||
.ciphers(null, IdentityCipherSuiteFilter.INSTANCE)
|
||||
.sessionCacheSize(0)
|
||||
.sessionTimeout(0)
|
||||
.build();
|
||||
|
||||
serverConnectedChannel = null;
|
||||
sb = new ServerBootstrap();
|
||||
cb = new Bootstrap();
|
||||
@ -897,24 +941,28 @@ public abstract class SSLEngineTest {
|
||||
File servertTrustCrtFile, File serverKeyFile, final File serverCrtFile, String serverKeyPassword,
|
||||
File clientTrustCrtFile, File clientKeyFile, File clientCrtFile, String clientKeyPassword)
|
||||
throws InterruptedException, SSLException {
|
||||
serverSslCtx = SslContextBuilder.forServer(serverCrtFile, serverKeyFile, serverKeyPassword)
|
||||
serverSslCtx = configureProtocolForMutualAuth(
|
||||
SslContextBuilder.forServer(serverCrtFile, serverKeyFile, serverKeyPassword)
|
||||
.sslProvider(sslServerProvider())
|
||||
.sslContextProvider(serverSslContextProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.trustManager(servertTrustCrtFile)
|
||||
.ciphers(null, IdentityCipherSuiteFilter.INSTANCE)
|
||||
.sessionCacheSize(0)
|
||||
.sessionTimeout(0)
|
||||
.build();
|
||||
|
||||
clientSslCtx = SslContextBuilder.forClient()
|
||||
.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();
|
||||
.sessionTimeout(0)).build();
|
||||
|
||||
serverConnectedChannel = null;
|
||||
sb = new ServerBootstrap();
|
||||
cb = new Bootstrap();
|
||||
@ -930,6 +978,7 @@ public abstract class SSLEngineTest {
|
||||
SSLEngine engine = serverSslCtx.newEngine(ch.alloc());
|
||||
engine.setUseClientMode(false);
|
||||
engine.setNeedClientAuth(true);
|
||||
|
||||
p.addLast(new SslHandler(engine));
|
||||
p.addLast(new MessageDelegatorChannelHandler(serverReceiver, serverLatch));
|
||||
p.addLast(new ChannelInboundHandlerAdapter() {
|
||||
@ -986,13 +1035,14 @@ public abstract class SSLEngineTest {
|
||||
protected void initChannel(Channel ch) throws Exception {
|
||||
ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), type));
|
||||
|
||||
SslHandler handler = clientSslCtx.newHandler(ch.alloc());
|
||||
handler.engine().setNeedClientAuth(true);
|
||||
ChannelPipeline p = ch.pipeline();
|
||||
p.addLast(clientSslCtx.newHandler(ch.alloc()));
|
||||
p.addLast(handler);
|
||||
p.addLast(new MessageDelegatorChannelHandler(clientReceiver, clientLatch));
|
||||
p.addLast(new ChannelInboundHandlerAdapter() {
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
cause.printStackTrace();
|
||||
if (cause.getCause() instanceof SSLHandshakeException) {
|
||||
clientException = cause.getCause();
|
||||
clientLatch.countDown();
|
||||
@ -1081,11 +1131,15 @@ public abstract class SSLEngineTest {
|
||||
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
||||
.sslProvider(sslClientProvider())
|
||||
.sslContextProvider(clientSslContextProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||
serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
||||
.sslProvider(sslServerProvider())
|
||||
.sslContextProvider(serverSslContextProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine clientEngine = null;
|
||||
SSLEngine serverEngine = null;
|
||||
@ -1110,11 +1164,15 @@ public abstract class SSLEngineTest {
|
||||
clientSslCtx = SslContextBuilder.forClient()
|
||||
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
||||
.sslProvider(sslClientProvider())
|
||||
// This test only works for non TLSv1.3 for now
|
||||
.protocols(PROTOCOL_TLS_V1_2)
|
||||
.sslContextProvider(clientSslContextProvider())
|
||||
.build();
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||
serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
||||
.sslProvider(sslServerProvider())
|
||||
// This test only works for non TLSv1.3 for now
|
||||
.protocols(PROTOCOL_TLS_V1_2)
|
||||
.sslContextProvider(serverSslContextProvider())
|
||||
.build();
|
||||
SSLEngine clientEngine = null;
|
||||
@ -1146,7 +1204,10 @@ public abstract class SSLEngineTest {
|
||||
final SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||
serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
||||
.sslProvider(sslServerProvider())
|
||||
.sslContextProvider(serverSslContextProvider()).build();
|
||||
.sslContextProvider(serverSslContextProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
sb = new ServerBootstrap()
|
||||
.group(new NioEventLoopGroup(1))
|
||||
.channel(NioServerSocketChannel.class)
|
||||
@ -1196,8 +1257,12 @@ public abstract class SSLEngineTest {
|
||||
serverChannel = sb.bind(new InetSocketAddress(0)).syncUninterruptibly().channel();
|
||||
|
||||
clientSslCtx = SslContextBuilder.forClient()
|
||||
.sslProvider(SslProvider.JDK) // OpenSslEngine doesn't support renegotiation on client side
|
||||
.trustManager(InsecureTrustManagerFactory.INSTANCE).build();
|
||||
// OpenSslEngine doesn't support renegotiation on client side
|
||||
.sslProvider(SslProvider.JDK)
|
||||
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
|
||||
cb = new Bootstrap();
|
||||
cb.group(new NioEventLoopGroup(1))
|
||||
@ -1259,6 +1324,8 @@ public abstract class SSLEngineTest {
|
||||
serverSslCtx = SslContextBuilder.forServer(serverCrtFile, serverKeyFile)
|
||||
.sslProvider(sslServerProvider())
|
||||
.sslContextProvider(serverSslContextProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
|
||||
sslEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
|
||||
@ -1338,7 +1405,10 @@ public abstract class SSLEngineTest {
|
||||
cTOsPos = cTOs.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();
|
||||
clientResult = clientEngine.unwrap(sTOc, clientAppReadBuffer);
|
||||
|
||||
@ -1350,7 +1420,7 @@ public abstract class SSLEngineTest {
|
||||
clientHandshakeFinished = true;
|
||||
}
|
||||
} else {
|
||||
assertFalse(sTOc.hasRemaining());
|
||||
assertEquals(0, sTOc.remaining());
|
||||
}
|
||||
|
||||
if (!serverHandshakeFinished) {
|
||||
@ -1433,24 +1503,35 @@ public abstract class SSLEngineTest {
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||
|
||||
try {
|
||||
setupHandlers(SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey(), null)
|
||||
SslContextBuilder serverCtxBuilder = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey(), null)
|
||||
.sslProvider(sslServerProvider())
|
||||
.sslContextProvider(serverSslContextProvider())
|
||||
.ciphers(null, IdentityCipherSuiteFilter.INSTANCE)
|
||||
.applicationProtocolConfig(serverApn)
|
||||
.sessionCacheSize(0)
|
||||
.sessionTimeout(0)
|
||||
.build(),
|
||||
.sessionTimeout(0);
|
||||
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())
|
||||
.sslContextProvider(clientSslContextProvider())
|
||||
.applicationProtocolConfig(clientApn)
|
||||
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
||||
.ciphers(null, IdentityCipherSuiteFilter.INSTANCE)
|
||||
.sessionCacheSize(0)
|
||||
.sessionTimeout(0)
|
||||
.build());
|
||||
.sessionTimeout(0);
|
||||
|
||||
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 {
|
||||
ssc.delete();
|
||||
}
|
||||
@ -1511,6 +1592,11 @@ public abstract class SSLEngineTest {
|
||||
ctx.fireExceptionCaught(cause);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
clientLatch.countDown();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -1524,12 +1610,15 @@ public abstract class SSLEngineTest {
|
||||
|
||||
@Test(timeout = 30000)
|
||||
public void testMutualAuthSameCertChain() throws Exception {
|
||||
serverSslCtx = SslContextBuilder.forServer(
|
||||
serverSslCtx = configureProtocolForMutualAuth(
|
||||
SslContextBuilder.forServer(
|
||||
new ByteArrayInputStream(X509_CERT_PEM.getBytes(CharsetUtil.UTF_8)),
|
||||
new ByteArrayInputStream(PRIVATE_KEY_PEM.getBytes(CharsetUtil.UTF_8)))
|
||||
.trustManager(new ByteArrayInputStream(X509_CERT_PEM.getBytes(CharsetUtil.UTF_8)))
|
||||
.clientAuth(ClientAuth.REQUIRE).sslProvider(sslServerProvider())
|
||||
.sslContextProvider(serverSslContextProvider()).build();
|
||||
.sslContextProvider(serverSslContextProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())).build();
|
||||
|
||||
sb = new ServerBootstrap();
|
||||
sb.group(new NioEventLoopGroup(), new NioEventLoopGroup());
|
||||
@ -1580,13 +1669,14 @@ public abstract class SSLEngineTest {
|
||||
}
|
||||
}).bind(new InetSocketAddress(0)).syncUninterruptibly().channel();
|
||||
|
||||
clientSslCtx = SslContextBuilder.forClient()
|
||||
.keyManager(
|
||||
clientSslCtx = configureProtocolForMutualAuth(
|
||||
SslContextBuilder.forClient().keyManager(
|
||||
new ByteArrayInputStream(CLIENT_X509_CERT_CHAIN_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)))
|
||||
.sslProvider(sslClientProvider())
|
||||
.sslContextProvider(clientSslContextProvider()).build();
|
||||
.sslContextProvider(clientSslContextProvider())
|
||||
.protocols(protocols()).ciphers(ciphers())).build();
|
||||
cb = new Bootstrap();
|
||||
cb.group(new NioEventLoopGroup());
|
||||
cb.channel(NioSocketChannel.class);
|
||||
@ -1610,12 +1700,16 @@ public abstract class SSLEngineTest {
|
||||
.forClient()
|
||||
.trustManager(cert.cert())
|
||||
.sslProvider(sslClientProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
|
||||
|
||||
serverSslCtx = SslContextBuilder
|
||||
.forServer(cert.certificate(), cert.privateKey())
|
||||
.sslProvider(sslServerProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
|
||||
|
||||
@ -1790,12 +1884,16 @@ public abstract class SSLEngineTest {
|
||||
.forClient()
|
||||
.trustManager(cert.cert())
|
||||
.sslProvider(sslClientProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
|
||||
|
||||
serverSslCtx = SslContextBuilder
|
||||
.forServer(cert.certificate(), cert.privateKey())
|
||||
.sslProvider(sslServerProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
|
||||
|
||||
@ -1829,6 +1927,8 @@ public abstract class SSLEngineTest {
|
||||
clientSslCtx = SslContextBuilder
|
||||
.forClient()
|
||||
.sslProvider(sslClientProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
|
||||
|
||||
@ -1857,6 +1957,8 @@ public abstract class SSLEngineTest {
|
||||
clientSslCtx = SslContextBuilder
|
||||
.forClient()
|
||||
.sslProvider(sslClientProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
|
||||
|
||||
@ -1881,12 +1983,16 @@ public abstract class SSLEngineTest {
|
||||
clientSslCtx = SslContextBuilder
|
||||
.forClient()
|
||||
.sslProvider(sslClientProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
|
||||
|
||||
serverSslCtx = SslContextBuilder
|
||||
.forServer(cert.certificate(), cert.privateKey())
|
||||
.sslProvider(sslServerProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
|
||||
|
||||
@ -1928,12 +2034,16 @@ public abstract class SSLEngineTest {
|
||||
clientSslCtx = SslContextBuilder
|
||||
.forClient()
|
||||
.sslProvider(sslClientProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
|
||||
|
||||
serverSslCtx = SslContextBuilder
|
||||
.forServer(cert.certificate(), cert.privateKey())
|
||||
.sslProvider(sslServerProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
|
||||
|
||||
@ -1965,12 +2075,16 @@ public abstract class SSLEngineTest {
|
||||
.forClient()
|
||||
.trustManager(cert.cert())
|
||||
.sslProvider(sslClientProvider())
|
||||
// This test only works for non TLSv1.3 for now
|
||||
.protocols(PROTOCOL_TLS_V1_2)
|
||||
.build();
|
||||
SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
|
||||
|
||||
serverSslCtx = SslContextBuilder
|
||||
.forServer(cert.certificate(), cert.privateKey())
|
||||
.sslProvider(sslServerProvider())
|
||||
// This test only works for non TLSv1.3 for now
|
||||
.protocols(PROTOCOL_TLS_V1_2)
|
||||
.build();
|
||||
SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
|
||||
|
||||
@ -2032,6 +2146,7 @@ public abstract class SSLEngineTest {
|
||||
|
||||
result = server.wrap(empty, encryptedServerToClient);
|
||||
encryptedServerToClient.flip();
|
||||
|
||||
assertEquals(SSLEngineResult.Status.CLOSED, result.getStatus());
|
||||
// UNWRAP/WRAP are not expected after this point
|
||||
assertEquals(SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, result.getHandshakeStatus());
|
||||
@ -2046,6 +2161,7 @@ public abstract class SSLEngineTest {
|
||||
assertTrue(server.isInboundDone());
|
||||
|
||||
result = client.unwrap(encryptedServerToClient, plainClientOut);
|
||||
|
||||
plainClientOut.flip();
|
||||
assertEquals(SSLEngineResult.Status.CLOSED, result.getStatus());
|
||||
// UNWRAP/WRAP are not expected after this point
|
||||
@ -2106,12 +2222,16 @@ public abstract class SSLEngineTest {
|
||||
.forClient()
|
||||
.trustManager(cert.cert())
|
||||
.sslProvider(sslClientProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
|
||||
|
||||
serverSslCtx = SslContextBuilder
|
||||
.forServer(cert.certificate(), cert.privateKey())
|
||||
.sslProvider(sslServerProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
|
||||
|
||||
@ -2145,12 +2265,16 @@ public abstract class SSLEngineTest {
|
||||
.forClient()
|
||||
.trustManager(cert.cert())
|
||||
.sslProvider(sslClientProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
|
||||
|
||||
serverSslCtx = SslContextBuilder
|
||||
.forServer(cert.certificate(), cert.privateKey())
|
||||
.sslProvider(sslServerProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
|
||||
|
||||
@ -2220,12 +2344,16 @@ public abstract class SSLEngineTest {
|
||||
.forClient()
|
||||
.trustManager(cert.cert())
|
||||
.sslProvider(sslClientProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
|
||||
|
||||
serverSslCtx = SslContextBuilder
|
||||
.forServer(cert.certificate(), cert.privateKey())
|
||||
.sslProvider(sslServerProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
|
||||
|
||||
@ -2240,21 +2368,35 @@ public abstract class SSLEngineTest {
|
||||
int srcLen = plainClientOut.remaining();
|
||||
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);
|
||||
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(srcLen, result.bytesConsumed());
|
||||
assertTrue(result.bytesProduced() > 0);
|
||||
|
||||
plainClientOut.clear();
|
||||
}
|
||||
|
||||
++count;
|
||||
} while (encClientToServer.position() < server.getSession().getPacketBufferSize());
|
||||
|
||||
// Check that we were able to wrap multiple times.
|
||||
assertTrue(count >= 2);
|
||||
encClientToServer.flip();
|
||||
|
||||
result = server.unwrap(encClientToServer, plainServerOut);
|
||||
assertEquals(SSLEngineResult.Status.OK, result.getStatus());
|
||||
assertTrue(result.bytesConsumed() > 0);
|
||||
assertTrue(result.bytesProduced() > 0);
|
||||
assertTrue(encClientToServer.hasRemaining());
|
||||
} finally {
|
||||
cert.delete();
|
||||
cleanupClientSslEngine(client);
|
||||
@ -2270,12 +2412,16 @@ public abstract class SSLEngineTest {
|
||||
.forClient()
|
||||
.trustManager(cert.cert())
|
||||
.sslProvider(sslClientProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
|
||||
|
||||
serverSslCtx = SslContextBuilder
|
||||
.forServer(cert.certificate(), cert.privateKey())
|
||||
.sslProvider(sslServerProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
|
||||
|
||||
@ -2341,12 +2487,16 @@ public abstract class SSLEngineTest {
|
||||
.forClient()
|
||||
.trustManager(cert.cert())
|
||||
.sslProvider(sslClientProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
|
||||
|
||||
serverSslCtx = SslContextBuilder
|
||||
.forServer(cert.certificate(), cert.privateKey())
|
||||
.sslProvider(sslServerProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
|
||||
|
||||
@ -2393,6 +2543,8 @@ public abstract class SSLEngineTest {
|
||||
SslContext ctx = SslContextBuilder
|
||||
.forServer(cert.certificate(), cert.privateKey())
|
||||
.sslProvider(sslServerProvider())
|
||||
.protocols(protocols())
|
||||
.ciphers(ciphers())
|
||||
.build();
|
||||
SSLEngine server = wrapEngine(ctx.newEngine(UnpooledByteBufAllocator.DEFAULT));
|
||||
|
||||
@ -2495,4 +2647,12 @@ public abstract class SSLEngineTest {
|
||||
protected SSLEngine wrapEngine(SSLEngine engine) {
|
||||
return engine;
|
||||
}
|
||||
|
||||
protected List<String> ciphers() {
|
||||
return Collections.singletonList(protocolCipherCombo.cipher);
|
||||
}
|
||||
|
||||
protected String[] protocols() {
|
||||
return new String[] { protocolCipherCombo.protocol };
|
||||
}
|
||||
}
|
||||
|
@ -15,18 +15,19 @@
|
||||
*/
|
||||
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.handler.ssl.util.SelfSignedCertificate;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLException;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class SslContextBuilderTest {
|
||||
|
||||
@Test
|
||||
@ -79,10 +80,19 @@ public class SslContextBuilderTest {
|
||||
testInvalidCipher(SslProvider.JDK);
|
||||
}
|
||||
|
||||
@Test(expected = SSLException.class)
|
||||
@Test
|
||||
public void testInvalidCipherOpenSSL() throws Exception {
|
||||
Assume.assumeTrue(OpenSsl.isAvailable());
|
||||
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 {
|
||||
|
@ -124,7 +124,8 @@ public class SslErrorTest {
|
||||
Assume.assumeTrue(OpenSsl.isAvailable());
|
||||
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||
final SslContext sslServerCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
||||
final SslContext sslServerCtx = OpenSslTestUtils.configureProtocolForMutualAuth(
|
||||
SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
||||
.sslProvider(serverProvider)
|
||||
.trustManager(new SimpleTrustManagerFactory() {
|
||||
@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)
|
||||
.keyManager(new File(getClass().getResource("test.crt").getFile()),
|
||||
new File(getClass().getResource("test_unencrypted.pem").getFile()))
|
||||
.sslProvider(clientProvider).build();
|
||||
.sslProvider(clientProvider), clientProvider, serverProvider).build();
|
||||
|
||||
Channel serverChannel = null;
|
||||
Channel clientChannel = null;
|
||||
|
@ -28,6 +28,7 @@ import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import static io.netty.handler.ssl.SslUtils.getEncryptedPacketLength;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class SslUtilsTest {
|
||||
@ -63,4 +64,15 @@ public class SslUtilsTest {
|
||||
engine.beginHandshake();
|
||||
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"));
|
||||
}
|
||||
|
||||
}
|
||||
|
2
pom.xml
2
pom.xml
@ -241,7 +241,7 @@
|
||||
<!-- Fedora-"like" systems. This is currently only used for the netty-tcnative dependency -->
|
||||
<os.detection.classifierWithLikes>fedora</os.detection.classifierWithLikes>
|
||||
<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>
|
||||
<conscrypt.groupId>org.conscrypt</conscrypt.groupId>
|
||||
<conscrypt.artifactId>conscrypt-openjdk-uber</conscrypt.artifactId>
|
||||
|
@ -138,7 +138,8 @@ public class SocketSslClientRenegotiateTest extends AbstractSocketTest {
|
||||
public void initChannel(Channel sch) throws Exception {
|
||||
serverChannel = sch;
|
||||
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("handler", serverHandler);
|
||||
}
|
||||
@ -150,7 +151,8 @@ public class SocketSslClientRenegotiateTest extends AbstractSocketTest {
|
||||
public void initChannel(Channel sch) throws Exception {
|
||||
clientChannel = sch;
|
||||
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("handler", clientHandler);
|
||||
}
|
||||
|
@ -123,17 +123,33 @@ public class SocketSslEchoTest extends AbstractSocketTest {
|
||||
"autoRead = {5}, useChunkedWriteHandler = {6}, useCompositeByteBuf = {7}")
|
||||
public static Collection<Object[]> data() throws Exception {
|
||||
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>();
|
||||
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();
|
||||
if (hasOpenSsl) {
|
||||
serverContexts.add(SslContextBuilder.forServer(CERT_FILE, KEY_FILE)
|
||||
.sslProvider(SslProvider.OPENSSL).build());
|
||||
clientContexts.add(SslContextBuilder.forClient().sslProvider(SslProvider.OPENSSL)
|
||||
.trustManager(CERT_FILE).build());
|
||||
.sslProvider(SslProvider.OPENSSL)
|
||||
// As we test renegotiation we should use a protocol that support it.
|
||||
.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 {
|
||||
logger.warn("OpenSSL is unavailable and thus will not be tested.", OpenSsl.unavailabilityCause());
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ public class SocketSslSessionReuseTest extends AbstractSocketTest {
|
||||
public void testSslSessionReuse(ServerBootstrap sb, Bootstrap cb) throws Throwable {
|
||||
final ReadAndDiscardHandler sh = new ReadAndDiscardHandler(true, 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>() {
|
||||
@Override
|
||||
|
Loading…
Reference in New Issue
Block a user