Allow to use TLSv1.3 with netty-tcnative withe java versions prior to 11. (#8394)

Motivation:

At the moment it's only possible to use TLSv1.3 with netty-tcnative if Java 11 is used. It should be possible to do so even with Java 8, 9 and 10.

Modification:

Add a workaround to be able to use TLSv1.3 also when using Java version prior to Java 11 and the default X509ExtendedTrustManager is used.

Result:

Be able to use TLSv1.3 also with past versions of Java.
This commit is contained in:
Norman Maurer 2018-10-18 13:50:12 +02:00 committed by GitHub
parent 47e4a58d90
commit 201e984cb3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 519 additions and 31 deletions

View File

@ -42,7 +42,6 @@ abstract class ExtendedOpenSslSession extends ExtendedSSLSession implements Open
private final OpenSslSession wrapped;
ExtendedOpenSslSession(OpenSslSession wrapped) {
assert !(wrapped instanceof ExtendedSSLSession);
this.wrapped = wrapped;
}
@ -153,7 +152,7 @@ abstract class ExtendedOpenSslSession extends ExtendedSSLSession implements Open
}
@Override
public final String getProtocol() {
public String getProtocol() {
return wrapped.getProtocol();
}

View File

@ -31,7 +31,6 @@ 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;
@ -40,16 +39,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import static io.netty.handler.ssl.SslUtils.DEFAULT_CIPHER_SUITES;
import static io.netty.handler.ssl.SslUtils.addIfSupported;
import static io.netty.handler.ssl.SslUtils.useFallbackCiphersIfDefaultIsEmpty;
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.PROTOCOL_TLS_V1_3;
import static io.netty.handler.ssl.SslUtils.*;
/**
* Tells if <a href="http://netty.io/wiki/forked-tomcat-native.html">{@code netty-tcnative}</a> and its OpenSSL support
@ -154,14 +144,13 @@ public final class OpenSsl {
long certBio = 0;
SelfSignedCertificate cert = null;
try {
if (PlatformDependent.javaVersion() >= 11) {
try {
SSLContext.setCipherSuite(sslCtx, TLSV13_CIPHERS, true);
tlsv13Supported = true;
} catch (Exception ignore) {
tlsv13Supported = false;
}
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);
@ -231,6 +220,8 @@ public final class OpenSsl {
}
addIfSupported(availableJavaCipherSuites, defaultCiphers, DEFAULT_CIPHER_SUITES);
addIfSupported(availableJavaCipherSuites, defaultCiphers, TLSV13_CIPHER_SUITES);
useFallbackCiphersIfDefaultIsEmpty(defaultCiphers, availableJavaCipherSuites);
DEFAULT_CIPHERS = Collections.unmodifiableList(defaultCiphers);
@ -266,11 +257,7 @@ public final class OpenSsl {
}
// 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
if (tlsv13Supported && doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1_3, SSL.SSL_OP_NO_TLSv1_3)) {
protocols.add(PROTOCOL_TLS_V1_3);
TLSV13_SUPPORTED = true;
} else {

View File

@ -0,0 +1,498 @@
/*
* Copyright 2018 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.ssl;
import io.netty.util.internal.EmptyArrays;
import io.netty.util.internal.PlatformDependent;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.X509ExtendedTrustManager;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.security.Principal;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;
/**
* Provide a way to use {@code TLSv1.3} with Java versions prior to 11 by adding a
* <a href="http://mail.openjdk.java.net/pipermail/security-dev/2018-September/018242.html>workaround</a> for the
* default {@link X509ExtendedTrustManager} implementations provided by the JDK that can not handle a protocol version
* of {@code TLSv1.3}.
*/
final class OpenSslTlsv13X509ExtendedTrustManager extends X509ExtendedTrustManager {
private final X509ExtendedTrustManager tm;
private OpenSslTlsv13X509ExtendedTrustManager(X509ExtendedTrustManager tm) {
this.tm = tm;
}
static X509ExtendedTrustManager wrap(X509ExtendedTrustManager tm, boolean client) {
if (PlatformDependent.javaVersion() < 11) {
X509Certificate[] certs = new X509Certificate[1];
try {
if (client) {
tm.checkServerTrusted(certs, "RSA", new DummySSLEngine(true));
} else {
tm.checkClientTrusted(certs, "RSA", new DummySSLEngine(false));
}
} catch (IllegalArgumentException e) {
// If this happened we failed because our protocol version was not known by the implementation.
// See http://mail.openjdk.java.net/pipermail/security-dev/2018-September/018242.html.
return new OpenSslTlsv13X509ExtendedTrustManager(tm);
} catch (Throwable ignore) {
// Just assume we do not need to wrap.
}
}
return tm;
}
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket)
throws CertificateException {
tm.checkClientTrusted(x509Certificates, s, socket);
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket)
throws CertificateException {
tm.checkServerTrusted(x509Certificates, s, socket);
}
private static SSLEngine wrapEngine(final SSLEngine engine) {
final SSLSession session = engine.getHandshakeSession();
if (session != null && SslUtils.PROTOCOL_TLS_V1_3.equals(session.getProtocol())) {
return new JdkSslEngine(engine) {
@Override
public String getNegotiatedApplicationProtocol() {
if (engine instanceof ApplicationProtocolAccessor) {
return ((ApplicationProtocolAccessor) engine).getNegotiatedApplicationProtocol();
}
return super.getNegotiatedApplicationProtocol();
}
@Override
public SSLSession getHandshakeSession() {
if (PlatformDependent.javaVersion() >= 7 && session instanceof ExtendedOpenSslSession) {
final ExtendedOpenSslSession extendedOpenSslSession = (ExtendedOpenSslSession) session;
return new ExtendedOpenSslSession(extendedOpenSslSession) {
@Override
public List getRequestedServerNames() {
return extendedOpenSslSession.getRequestedServerNames();
}
@Override
public String[] getPeerSupportedSignatureAlgorithms() {
return extendedOpenSslSession.getPeerSupportedSignatureAlgorithms();
}
@Override
public String getProtocol() {
return SslUtils.PROTOCOL_TLS_V1_2;
}
};
} else {
return new SSLSession() {
@Override
public byte[] getId() {
return session.getId();
}
@Override
public SSLSessionContext getSessionContext() {
return session.getSessionContext();
}
@Override
public long getCreationTime() {
return session.getCreationTime();
}
@Override
public long getLastAccessedTime() {
return session.getLastAccessedTime();
}
@Override
public void invalidate() {
session.invalidate();
}
@Override
public boolean isValid() {
return session.isValid();
}
@Override
public void putValue(String s, Object o) {
session.putValue(s, o);
}
@Override
public Object getValue(String s) {
return session.getValue(s);
}
@Override
public void removeValue(String s) {
session.removeValue(s);
}
@Override
public String[] getValueNames() {
return session.getValueNames();
}
@Override
public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
return session.getPeerCertificates();
}
@Override
public Certificate[] getLocalCertificates() {
return session.getLocalCertificates();
}
@Override
public javax.security.cert.X509Certificate[] getPeerCertificateChain()
throws SSLPeerUnverifiedException {
return session.getPeerCertificateChain();
}
@Override
public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
return session.getPeerPrincipal();
}
@Override
public Principal getLocalPrincipal() {
return session.getLocalPrincipal();
}
@Override
public String getCipherSuite() {
return session.getCipherSuite();
}
@Override
public String getProtocol() {
return SslUtils.PROTOCOL_TLS_V1_2;
}
@Override
public String getPeerHost() {
return session.getPeerHost();
}
@Override
public int getPeerPort() {
return session.getPeerPort();
}
@Override
public int getPacketBufferSize() {
return session.getPacketBufferSize();
}
@Override
public int getApplicationBufferSize() {
return session.getApplicationBufferSize();
}
};
}
}
};
}
return engine;
}
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, final String s, SSLEngine sslEngine)
throws CertificateException {
tm.checkClientTrusted(x509Certificates, s, wrapEngine(sslEngine));
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine)
throws CertificateException {
tm.checkServerTrusted(x509Certificates, s, wrapEngine(sslEngine));
}
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
tm.checkClientTrusted(x509Certificates, s);
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
tm.checkServerTrusted(x509Certificates, s);
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return tm.getAcceptedIssuers();
}
private static final class DummySSLEngine extends SSLEngine {
private final boolean client;
DummySSLEngine(boolean client) {
this.client = client;
}
@Override
public SSLSession getHandshakeSession() {
return new SSLSession() {
@Override
public byte[] getId() {
return EmptyArrays.EMPTY_BYTES;
}
@Override
public SSLSessionContext getSessionContext() {
return null;
}
@Override
public long getCreationTime() {
return 0;
}
@Override
public long getLastAccessedTime() {
return 0;
}
@Override
public void invalidate() {
// NOOP
}
@Override
public boolean isValid() {
return false;
}
@Override
public void putValue(String s, Object o) {
// NOOP
}
@Override
public Object getValue(String s) {
return null;
}
@Override
public void removeValue(String s) {
// NOOP
}
@Override
public String[] getValueNames() {
return EmptyArrays.EMPTY_STRINGS;
}
@Override
public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
return EmptyArrays.EMPTY_CERTIFICATES;
}
@Override
public Certificate[] getLocalCertificates() {
return EmptyArrays.EMPTY_CERTIFICATES;
}
@Override
public javax.security.cert.X509Certificate[] getPeerCertificateChain()
throws SSLPeerUnverifiedException {
return EmptyArrays.EMPTY_JAVAX_X509_CERTIFICATES;
}
@Override
public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
return null;
}
@Override
public Principal getLocalPrincipal() {
return null;
}
@Override
public String getCipherSuite() {
return null;
}
@Override
public String getProtocol() {
return SslUtils.PROTOCOL_TLS_V1_3;
}
@Override
public String getPeerHost() {
return null;
}
@Override
public int getPeerPort() {
return 0;
}
@Override
public int getPacketBufferSize() {
return 0;
}
@Override
public int getApplicationBufferSize() {
return 0;
}
};
}
@Override
public SSLEngineResult wrap(ByteBuffer[] byteBuffers, int i, int i1, ByteBuffer byteBuffer)
throws SSLException {
throw new UnsupportedOperationException();
}
@Override
public SSLEngineResult unwrap(ByteBuffer byteBuffer, ByteBuffer[] byteBuffers, int i, int i1)
throws SSLException {
throw new UnsupportedOperationException();
}
@Override
public Runnable getDelegatedTask() {
return null;
}
@Override
public void closeInbound() throws SSLException {
// NOOP
}
@Override
public boolean isInboundDone() {
return true;
}
@Override
public void closeOutbound() {
// NOOP
}
@Override
public boolean isOutboundDone() {
return true;
}
@Override
public String[] getSupportedCipherSuites() {
return EmptyArrays.EMPTY_STRINGS;
}
@Override
public String[] getEnabledCipherSuites() {
return EmptyArrays.EMPTY_STRINGS;
}
@Override
public void setEnabledCipherSuites(String[] strings) {
// NOOP
}
@Override
public String[] getSupportedProtocols() {
return new String[] { SslUtils.PROTOCOL_TLS_V1_3 };
}
@Override
public String[] getEnabledProtocols() {
return new String[] { SslUtils.PROTOCOL_TLS_V1_3 };
}
@Override
public void setEnabledProtocols(String[] strings) {
// NOOP
}
@Override
public SSLSession getSession() {
return getHandshakeSession();
}
@Override
public void beginHandshake() throws SSLException {
// NOOP
}
@Override
public HandshakeStatus getHandshakeStatus() {
return HandshakeStatus.NEED_TASK;
}
@Override
public void setUseClientMode(boolean b) {
// NOOP
}
@Override
public boolean getUseClientMode() {
return client;
}
@Override
public void setNeedClientAuth(boolean b) {
// NOOP
}
@Override
public boolean getNeedClientAuth() {
return false;
}
@Override
public void setWantClientAuth(boolean b) {
// NOOP
}
@Override
public boolean getWantClientAuth() {
return false;
}
@Override
public void setEnableSessionCreation(boolean b) {
// NOOP
}
@Override
public boolean getEnableSessionCreation() {
return false;
}
}
}

View File

@ -233,7 +233,7 @@ public final class ReferenceCountedOpenSslClientContext extends ReferenceCounted
ExtendedTrustManagerVerifyCallback(OpenSslEngineMap engineMap, X509ExtendedTrustManager manager) {
super(engineMap);
this.manager = manager;
this.manager = OpenSslTlsv13X509ExtendedTrustManager.wrap(manager, true);
}
@Override

View File

@ -239,7 +239,7 @@ public final class ReferenceCountedOpenSslServerContext extends ReferenceCounted
ExtendedTrustManagerVerifyCallback(OpenSslEngineMap engineMap, X509ExtendedTrustManager manager) {
super(engineMap);
this.manager = manager;
this.manager = OpenSslTlsv13X509ExtendedTrustManager.wrap(manager, false);
}
@Override

View File

@ -97,10 +97,11 @@ final class SslUtils {
static final String[] DEFAULT_CIPHER_SUITES;
static final String[] DEFAULT_TLSV13_CIPHER_SUITES;
static final String[] TLSV13_CIPHER_SUITES = { "TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384" };
static {
if (PlatformDependent.javaVersion() >= 11) {
DEFAULT_TLSV13_CIPHER_SUITES = new String[] { "TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384" };
DEFAULT_TLSV13_CIPHER_SUITES = TLSV13_CIPHER_SUITES;
} else {
DEFAULT_TLSV13_CIPHER_SUITES = EmptyArrays.EMPTY_STRINGS;
}

View File

@ -73,7 +73,7 @@ public class OpenSslEngineTest extends SSLEngineTest {
for (BufferType type: BufferType.values()) {
params.add(new Object[] { type, ProtocolCipherCombo.tlsv12()});
if (PlatformDependent.javaVersion() >= 11 && OpenSsl.isTlsv13Supported()) {
if (OpenSsl.isTlsv13Supported()) {
params.add(new Object[] { type, ProtocolCipherCombo.tlsv13() });
}
}

View File

@ -22,7 +22,9 @@ public class OpenSslTest {
@Test
public void testDefaultCiphers() {
Assert.assertTrue(
OpenSsl.DEFAULT_CIPHERS.size() <= SslUtils.DEFAULT_CIPHER_SUITES.length);
if (!OpenSsl.isTlsv13Supported()) {
Assert.assertTrue(
OpenSsl.DEFAULT_CIPHERS.size() <= SslUtils.DEFAULT_CIPHER_SUITES.length);
}
}
}

View File

@ -1201,6 +1201,7 @@ public abstract class SSLEngineTest {
@Test(timeout = 30000)
public void clientInitiatedRenegotiationWithFatalAlertDoesNotInfiniteLoopServer()
throws CertificateException, SSLException, InterruptedException, ExecutionException {
Assume.assumeTrue(PlatformDependent.javaVersion() >= 11);
final SelfSignedCertificate ssc = new SelfSignedCertificate();
serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
.sslProvider(sslServerProvider())