Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Netty tries to use TLSv1.3 on WebSphere where it is not supported #11589

Closed
mormegil-cz opened this issue Aug 17, 2021 · 7 comments · Fixed by #11604
Closed

Netty tries to use TLSv1.3 on WebSphere where it is not supported #11589

mormegil-cz opened this issue Aug 17, 2021 · 7 comments · Fixed by #11604
Assignees
Milestone

Comments

@mormegil-cz
Copy link

Expected behavior

On IBM WebSphere with Java 8 with no TLS 1.3 support, Netty should connect using TLS 1.2.

Actual behavior

Netty tries to connect using TLS 1.3 and dies with java.lang.IllegalArgumentException: TLSv1.3 (with client certificates used).

Steps to reproduce

We are using pushy (0.14.2) which connects to APNS’s API (authorized using a client certificate) using netty. When the code is run on IBM WebSphere server, the push messages are not sent, as the sending dies with:

Exception call stack
Caused by: io.netty.handler.codec.DecoderException: java.lang.IllegalArgumentException: TLSv1.3
	at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:478)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	at io.netty.handler.proxy.ProxyHandler.channelRead(ProxyHandler.java:253)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)
	at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:253)
	at io.netty.handler.proxy.HttpProxyHandler$HttpClientCodecWrapper.channelRead(HttpProxyHandler.java:272)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:719)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:655)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:581)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.lang.Thread.run(Thread.java:818)
Caused by: java.lang.IllegalArgumentException: TLSv1.3
	at com.ibm.jsse2.U.a(U.java:56)
	at com.ibm.jsse2.az.a(az.java:201)
	at com.ibm.jsse2.az.chooseEngineClientAlias(az.java:109)
	at io.netty.handler.ssl.OpenSslKeyMaterialManager.chooseClientAlias(OpenSslKeyMaterialManager.java:126)
	at io.netty.handler.ssl.OpenSslKeyMaterialManager.setKeyMaterialClientSide(OpenSslKeyMaterialManager.java:95)
	at io.netty.handler.ssl.ReferenceCountedOpenSslClientContext$OpenSslClientCertificateCallback.handle(ReferenceCountedOpenSslClientContext.java:266)
	at io.netty.internal.tcnative.SSL.readFromSSL(Native Method)
	at io.netty.handler.ssl.ReferenceCountedOpenSslEngine.readPlaintextData(ReferenceCountedOpenSslEngine.java:634)
	at io.netty.handler.ssl.ReferenceCountedOpenSslEngine.unwrap(ReferenceCountedOpenSslEngine.java:1258)
	at io.netty.handler.ssl.ReferenceCountedOpenSslEngine.unwrap(ReferenceCountedOpenSslEngine.java:1384)
	at io.netty.handler.ssl.ReferenceCountedOpenSslEngine.unwrap(ReferenceCountedOpenSslEngine.java:1427)
	at io.netty.handler.ssl.SslHandler$SslEngineType$1.unwrap(SslHandler.java:208)
	at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1395)
	at io.netty.handler.ssl.SslHandler.decodeNonJdkCompatible(SslHandler.java:1302)
	at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1339)
	at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:508)
	at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:447)
	... 27 common frames omitted

The problem seems to be that Netty tries to detect whether TLS 1.3 is supported, or not. However, for the OPENSSL_REFCNT provider, TLS 1.3 is supported (provided by the native binary), but still it does not work, because the problem is not in the OpenSSL code (which would probably work fine with TLS 1.3) but in the X509ExtendedKeyManager.chooseEngineClientAlias which calls to the implementation of the X509ExtendedKeyManager provided by the built-in IBMJSSEProvider2 security provider. Apparently, this provider determines the protocol of the getHandshakeSession of the SSLEngine and dies as soon as it finds out the protocol is TLSv1.3 which is unknown to him. (Even if the used keystore contains a single alias, so there is nothing to choose from…)

So, basically, OPENSSL_REFCNT should not claim that isTlsv13Supported=true if it defers part of its job to the built-in implementation of X509KeyManager which does not support TLS 1.3 connections.

Right now, we are not sure how we’ll work around the problem. I think we’ll either downgrade Netty, or fork/patch it with a TLS13-hard-disable flag (because IIANM there is no way to force Netty from the outside (e.g. by a JVM system property) not to use TLS 1.3).

See also jchambers/pushy#864

Minimal yet complete reproducer code (or URL to code)

I’m afraid I’m not able to provide a complete reproducer code, as it involves sending push messages with pushy. Basically, pushy seems to create the context using

final SslContextBuilder sslContextBuilder = SslContextBuilder.forClient()
        .sslProvider(SslProvider.OPENSSL_REFCNT)
        .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE);
sslContextBuilder.keyManager(this.privateKey, this.privateKeyPassword, this.clientCertificate);
sslContext = sslContextBuilder.build();

Netty version

4.1.61.Final

JVM version (e.g. java -version)

1.8.0_261, Java Runtime Version = 8.0.6.16 - pxa6480sr6fp16-20200902_01(SR6 FP16), Java Compiler = j9jit29, Java VM name = IBM J9 VM

OS version (e.g. uname -a)

Ubuntu 18.04.5 LTS

@hyperxpro
Copy link
Contributor

You can choose what protocols you want to use in SslContextBuilder.

@mormegil-cz
Copy link
Author

Well, yes, but I don’t call SslContextBuilder, Pushy does (so I might fork/patch Pushy instead of Netty… same difference). That’s why I said “from the outside” – I’d need some global flag, environment variable, JVM parameter, etc.

@hyperxpro
Copy link
Contributor

I’d need some global flag, environment variable, JVM parameter, etc.

Unfortunately, that's not possible. You probably have to fork or extend the class which builds SslContextBuilder.

@normanmaurer
Copy link
Member

I guess we could at least respect jdk.tls.client.protocols and jdk.tls.server.protocols, @mormegil-cz would this solve the problem ?

@mormegil-cz
Copy link
Author

Sure, that would be a good workaround, exactly what I had in mind.

(Note that I still believe there is a bug in Netty: it should not try to connect using TLS 1.3 and throwing exception in the process, if connecting using TLS 1.2 would work fine. So either Netty should autodetect TLS 1.3 won’t work and use 1.2, or reimplement the missing pieces by itself, so that TLS 1.3 would work even without JRE support.)

@normanmaurer
Copy link
Member

@mormegil-cz honestly I think its a bug in IBM J9. It should not throw... Anyway let me see if we can make it a bit more robust and handle even this case. Can you do me a favour and let me know what this code prints out of IBM J9 ?:

System.out.println(SslProvider.isTlsv13Supported(SslProvider.JDK));

@mormegil-cz
Copy link
Author

@normanmaurer It prints false. I tested it using a small console app running on IBM Java (i.e. not as an enterprise application inside the WebSphere), which printed:

java.version: 1.8.0_241
java.vendor: IBM Corporation
java.vm.name: IBM J9 VM
java.vm.version: 2.9
java.runtime.name: Java(TM) SE Runtime Environment
java.runtime.version: 8.0.6.6 - pxa6480sr6fp6-20200303_01(SR6 FP6)
---
SslProvider.isTlsv13Supported(SslProvider.JDK): false
SslProvider.isTlsv13Supported(SslProvider.OPENSSL_REFCNT): true
Segmentation fault

(Yeah, segmentation fault, no idea what is wrong, but hopefully, for such a simple test, this does not matter, dunno.)

normanmaurer added a commit that referenced this issue Aug 19, 2021
Motivation:

We tightly integrate the TrustManger and KeyManager into our native SSL implementation which means that both of them need to support TLSv1.3 as protocol. This is not always the case and so can produce runtime exceptions.

As TLSv1.3 support was backported to Java8 quite some time now we should be a bit more conservative and only enable TLSv1.3 for our native implementation if the JDK implementation supports it as well. This also allows us to remove some hacks we had in place to be able to support it before in Java8.

Modifications:

- Only enable TLSv1.3 support for our native SSL implementation when the JDK supports it as well
- Remove OpenSslTlsv13X509ExtendedTrustManager as its not needed anymore

Result:

Fixes #11589
@normanmaurer normanmaurer added this to the 4.1.68.Final milestone Aug 19, 2021
@normanmaurer normanmaurer self-assigned this Aug 19, 2021
normanmaurer added a commit that referenced this issue Aug 20, 2021
Motivation:

We tightly integrate the TrustManger and KeyManager into our native SSL implementation which means that both of them need to support TLSv1.3 as protocol. This is not always the case and so can produce runtime exceptions.

As TLSv1.3 support was backported to Java8 quite some time now we should be a bit more conservative and only enable TLSv1.3 for our native implementation if the JDK implementation supports it as well. This also allows us to remove some hacks we had in place to be able to support it before in Java8.

Modifications:

- Only enable TLSv1.3 support for our native SSL implementation when the JDK supports it as well
- Remove OpenSslTlsv13X509ExtendedTrustManager as its not needed anymore

Result:

Fixes #11589
normanmaurer added a commit that referenced this issue Aug 20, 2021
Motivation:

We tightly integrate the TrustManger and KeyManager into our native SSL implementation which means that both of them need to support TLSv1.3 as protocol. This is not always the case and so can produce runtime exceptions.

As TLSv1.3 support was backported to Java8 quite some time now we should be a bit more conservative and only enable TLSv1.3 for our native implementation if the JDK implementation supports it as well. This also allows us to remove some hacks we had in place to be able to support it before in Java8.

Modifications:

- Only enable TLSv1.3 support for our native SSL implementation when the JDK supports it as well
- Remove OpenSslTlsv13X509ExtendedTrustManager as its not needed anymore

Result:

Fixes #11589
laosijikaichele pushed a commit to laosijikaichele/netty that referenced this issue Dec 16, 2021
Motivation:

We tightly integrate the TrustManger and KeyManager into our native SSL implementation which means that both of them need to support TLSv1.3 as protocol. This is not always the case and so can produce runtime exceptions.

As TLSv1.3 support was backported to Java8 quite some time now we should be a bit more conservative and only enable TLSv1.3 for our native implementation if the JDK implementation supports it as well. This also allows us to remove some hacks we had in place to be able to support it before in Java8.

Modifications:

- Only enable TLSv1.3 support for our native SSL implementation when the JDK supports it as well
- Remove OpenSslTlsv13X509ExtendedTrustManager as its not needed anymore

Result:

Fixes netty#11589
laosijikaichele pushed a commit to laosijikaichele/netty that referenced this issue Dec 16, 2021
Motivation:

We tightly integrate the TrustManger and KeyManager into our native SSL implementation which means that both of them need to support TLSv1.3 as protocol. This is not always the case and so can produce runtime exceptions.

As TLSv1.3 support was backported to Java8 quite some time now we should be a bit more conservative and only enable TLSv1.3 for our native implementation if the JDK implementation supports it as well. This also allows us to remove some hacks we had in place to be able to support it before in Java8.

Modifications:

- Only enable TLSv1.3 support for our native SSL implementation when the JDK supports it as well
- Remove OpenSslTlsv13X509ExtendedTrustManager as its not needed anymore

Result:

Fixes netty#11589
raidyue pushed a commit to raidyue/netty that referenced this issue Jul 8, 2022
Motivation:

We tightly integrate the TrustManger and KeyManager into our native SSL implementation which means that both of them need to support TLSv1.3 as protocol. This is not always the case and so can produce runtime exceptions.

As TLSv1.3 support was backported to Java8 quite some time now we should be a bit more conservative and only enable TLSv1.3 for our native implementation if the JDK implementation supports it as well. This also allows us to remove some hacks we had in place to be able to support it before in Java8.

Modifications:

- Only enable TLSv1.3 support for our native SSL implementation when the JDK supports it as well
- Remove OpenSslTlsv13X509ExtendedTrustManager as its not needed anymore

Result:

Fixes netty#11589
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants