From 201e984cb3995d59cf8254f851f0ffb9090c2fea Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 18 Oct 2018 13:50:12 +0200 Subject: [PATCH] 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. --- .../handler/ssl/ExtendedOpenSslSession.java | 3 +- .../java/io/netty/handler/ssl/OpenSsl.java | 33 +- ...OpenSslTlsv13X509ExtendedTrustManager.java | 498 ++++++++++++++++++ .../ReferenceCountedOpenSslClientContext.java | 2 +- .../ReferenceCountedOpenSslServerContext.java | 2 +- .../java/io/netty/handler/ssl/SslUtils.java | 3 +- .../netty/handler/ssl/OpenSslEngineTest.java | 2 +- .../io/netty/handler/ssl/OpenSslTest.java | 6 +- .../io/netty/handler/ssl/SSLEngineTest.java | 1 + 9 files changed, 519 insertions(+), 31 deletions(-) create mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslTlsv13X509ExtendedTrustManager.java diff --git a/handler/src/main/java/io/netty/handler/ssl/ExtendedOpenSslSession.java b/handler/src/main/java/io/netty/handler/ssl/ExtendedOpenSslSession.java index 69a6125d17..184845a9fe 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ExtendedOpenSslSession.java +++ b/handler/src/main/java/io/netty/handler/ssl/ExtendedOpenSslSession.java @@ -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(); } diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java index 955404b25e..073c855a4a 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java @@ -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 {@code netty-tcnative} 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 { diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslTlsv13X509ExtendedTrustManager.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslTlsv13X509ExtendedTrustManager.java new file mode 100644 index 0000000000..e145c05c08 --- /dev/null +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslTlsv13X509ExtendedTrustManager.java @@ -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 + * = 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; + } + } +} diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java index 4972905baa..f508971f01 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java @@ -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 diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java index 1dbae15dd3..e901aebb38 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java @@ -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 diff --git a/handler/src/main/java/io/netty/handler/ssl/SslUtils.java b/handler/src/main/java/io/netty/handler/ssl/SslUtils.java index f1f000fa8f..492c4f2b7d 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslUtils.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslUtils.java @@ -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; } diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java index 630de226d6..1ae88ebdfb 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java @@ -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() }); } } diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslTest.java index 4fd8491099..0a9d19db65 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslTest.java @@ -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); + } } } diff --git a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java index 0094c92d3e..fd8c9b8fc3 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java @@ -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())