[ 
https://issues.apache.org/jira/browse/ZOOKEEPER-5053?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Dávid Paksy updated ZOOKEEPER-5053:
-----------------------------------
    Description: 
When you have a ZooKeeper cluster in FIPS mode, and set the TLS protocol to 
TLSv1.3:
{noformat}
ssl.enabledProtocols=TLSv1.3
ssl.quorum.enabledProtocols=TLSv1.3
{noformat}
And on the client side there is no TLS protocol configured, when the client 
tries to connect to the server, it will fail because of a TLS protocol mismatch.
{noformat}
2026-06-01 15:48:28,489 ERROR 
org.apache.zookeeper.server.NettyServerCnxnFactory: Unsuccessful handshake with 
session 0x0
2026-06-01 15:48:28,489 WARN 
org.apache.zookeeper.server.NettyServerCnxnFactory: Exception caught
io.netty.handler.codec.DecoderException: javax.net.ssl.SSLException: 
org.bouncycastle.tls.TlsFatalAlert: protocol_version(70)
        at 
io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:515)
        at 
io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:296)
        at 
io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
        at 
io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
        at 
io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
        at 
io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1357)
        at 
io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
        at 
io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
        at 
io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:868)
        at 
io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:805)
        at 
io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:501)
        at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:399)
        at 
io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:998)
        at 
io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
        at 
io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.base/java.lang.Thread.run(Thread.java:840)
Caused by: javax.net.ssl.SSLException: org.bouncycastle.tls.TlsFatalAlert: 
protocol_version(70)
        at 
bctls/org.bouncycastle.jsse.provider.ProvSSLEngine.unwrap(ProvSSLEngine.java:502)
        at java.base/javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:679)
        at 
io.netty.handler.ssl.SslHandler$SslEngineType$3.unwrap(SslHandler.java:308)
        at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1486)
        at 
io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1377)
        at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1428)
        at 
io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:545)
        at 
io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:484)
        ... 15 more
Caused by: org.bouncycastle.tls.TlsFatalAlert: protocol_version(70)
        at 
bctls/org.bouncycastle.tls.AbstractTlsServer.getServerVersion(AbstractTlsServer.java:413)
        at 
bctls/org.bouncycastle.jsse.provider.ProvTlsServer.getServerVersion(ProvTlsServer.java:767)
        at 
bctls/org.bouncycastle.tls.TlsServerProtocol.generateServerHello(TlsServerProtocol.java:508)
        at 
bctls/org.bouncycastle.tls.TlsServerProtocol.handleHandshakeMessage(TlsServerProtocol.java:1058)
        at 
bctls/org.bouncycastle.tls.TlsProtocol.processHandshakeQueue(TlsProtocol.java:715)
        at 
bctls/org.bouncycastle.tls.TlsProtocol.processRecord(TlsProtocol.java:591)
        at 
bctls/org.bouncycastle.tls.RecordStream.readFullRecord(RecordStream.java:209)
        at 
bctls/org.bouncycastle.tls.TlsProtocol.safeReadFullRecord(TlsProtocol.java:926)
        at 
bctls/org.bouncycastle.tls.TlsProtocol.offerInput(TlsProtocol.java:1368)
        at 
bctls/org.bouncycastle.jsse.provider.ProvSSLEngine.unwrap(ProvSSLEngine.java:486){noformat}
 

This is because on client side in FIPS mode we create the SSLContext with 
TLSv1.2 but the server uses TLSv1.3 and they cannot negotiate a common protocol.

If we'd like to have a SSLContext which supports multiple TLS versions on the 
client side, we should call SSLContext.getProtocol("TLS") instead of what we 
have right now:
{code:java}
        String defaultTlsProtocol = defaultTlsProtocol(config);
        String protocol = config.getProperty(sslProtocolProperty, 
defaultTlsProtocol);
        try {
            SSLContext sslContext = SSLContext.getInstance(protocol);
            sslContext.init(keyManagers, trustManagers, null);
            return new SSLContextAndOptions(this, config, sslContext);
        } catch (NoSuchAlgorithmException | KeyManagementException 
sslContextInitException) {
            throw new SSLContextException(sslContextInitException);
        }
{code}
 

Note: This only affects branch-3.8 because in newer branches the Netty-related 
code was refactored  https://issues.apache.org/jira/browse/ZOOKEEPER-4622 so 
this issue does not come on 3.9.

  was:
When you have a ZooKeeper cluster in FIPS mode, and set the TLS protocol to 
TLSv1.3:

{noformat}
ssl.enabledProtocols=TLSv1.3
ssl.quorum.enabledProtocols=TLSv1.3
{noformat}

And on the client side there is no TLS protocol configured, when the client 
tries to connect to the server, it will fail because of a TLS protocol mismatch.

{noformat}
2026-06-01 15:48:28,489 ERROR 
org.apache.zookeeper.server.NettyServerCnxnFactory: Unsuccessful handshake with 
session 0x0
2026-06-01 15:48:28,489 WARN 
org.apache.zookeeper.server.NettyServerCnxnFactory: Exception caught
io.netty.handler.codec.DecoderException: javax.net.ssl.SSLException: 
org.bouncycastle.tls.TlsFatalAlert: protocol_version(70)
        at 
io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:515)
        at 
io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:296)
        at 
io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
        at 
io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
        at 
io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
        at 
io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1357)
        at 
io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
        at 
io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
        at 
io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:868)
        at 
io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:805)
        at 
io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:501)
        at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:399)
        at 
io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:998)
        at 
io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
        at 
io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.base/java.lang.Thread.run(Thread.java:840)
Caused by: javax.net.ssl.SSLException: org.bouncycastle.tls.TlsFatalAlert: 
protocol_version(70)
        at 
bctls/org.bouncycastle.jsse.provider.ProvSSLEngine.unwrap(ProvSSLEngine.java:502)
        at java.base/javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:679)
        at 
io.netty.handler.ssl.SslHandler$SslEngineType$3.unwrap(SslHandler.java:308)
        at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1486)
        at 
io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1377)
        at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1428)
        at 
io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:545)
        at 
io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:484)
        ... 15 more
Caused by: org.bouncycastle.tls.TlsFatalAlert: protocol_version(70)
        at 
bctls/org.bouncycastle.tls.AbstractTlsServer.getServerVersion(AbstractTlsServer.java:413)
        at 
bctls/org.bouncycastle.jsse.provider.ProvTlsServer.getServerVersion(ProvTlsServer.java:767)
        at 
bctls/org.bouncycastle.tls.TlsServerProtocol.generateServerHello(TlsServerProtocol.java:508)
        at 
bctls/org.bouncycastle.tls.TlsServerProtocol.handleHandshakeMessage(TlsServerProtocol.java:1058)
        at 
bctls/org.bouncycastle.tls.TlsProtocol.processHandshakeQueue(TlsProtocol.java:715)
        at 
bctls/org.bouncycastle.tls.TlsProtocol.processRecord(TlsProtocol.java:591)
        at 
bctls/org.bouncycastle.tls.RecordStream.readFullRecord(RecordStream.java:209)
        at 
bctls/org.bouncycastle.tls.TlsProtocol.safeReadFullRecord(TlsProtocol.java:926)
        at 
bctls/org.bouncycastle.tls.TlsProtocol.offerInput(TlsProtocol.java:1368)
        at 
bctls/org.bouncycastle.jsse.provider.ProvSSLEngine.unwrap(ProvSSLEngine.java:486){noformat}
 

This is because on client side in FIPS mode we create the SSLContext with 
TLSv1.2 but the server uses TLSv1.3 and they cannot negotiate a common protocol.

If we'd like to have a SSLContext which supports multiple TLS versions on the 
client side, we should call SSLContext.getProtocol("TLS") instead of what we 
have right now:

{code:java}
        String defaultTlsProtocol = defaultTlsProtocol(config);
        String protocol = config.getProperty(sslProtocolProperty, 
defaultTlsProtocol);
        try {
            SSLContext sslContext = SSLContext.getInstance(protocol);
            sslContext.init(keyManagers, trustManagers, null);
            return new SSLContextAndOptions(this, config, sslContext);
        } catch (NoSuchAlgorithmException | KeyManagementException 
sslContextInitException) {
            throw new SSLContextException(sslContextInitException);
        }
{code}


> Support multiple TLS versions in ZooKeeper client
> -------------------------------------------------
>
>                 Key: ZOOKEEPER-5053
>                 URL: https://issues.apache.org/jira/browse/ZOOKEEPER-5053
>             Project: ZooKeeper
>          Issue Type: Bug
>          Components: java client
>    Affects Versions: 3.8.5
>            Reporter: Dávid Paksy
>            Priority: Major
>
> When you have a ZooKeeper cluster in FIPS mode, and set the TLS protocol to 
> TLSv1.3:
> {noformat}
> ssl.enabledProtocols=TLSv1.3
> ssl.quorum.enabledProtocols=TLSv1.3
> {noformat}
> And on the client side there is no TLS protocol configured, when the client 
> tries to connect to the server, it will fail because of a TLS protocol 
> mismatch.
> {noformat}
> 2026-06-01 15:48:28,489 ERROR 
> org.apache.zookeeper.server.NettyServerCnxnFactory: Unsuccessful handshake 
> with session 0x0
> 2026-06-01 15:48:28,489 WARN 
> org.apache.zookeeper.server.NettyServerCnxnFactory: Exception caught
> io.netty.handler.codec.DecoderException: javax.net.ssl.SSLException: 
> org.bouncycastle.tls.TlsFatalAlert: protocol_version(70)
>       at 
> io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:515)
>       at 
> io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:296)
>       at 
> io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
>       at 
> io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
>       at 
> io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
>       at 
> io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1357)
>       at 
> io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
>       at 
> io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
>       at 
> io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:868)
>       at 
> io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:805)
>       at 
> io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:501)
>       at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:399)
>       at 
> io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:998)
>       at 
> io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
>       at 
> io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
>       at java.base/java.lang.Thread.run(Thread.java:840)
> Caused by: javax.net.ssl.SSLException: org.bouncycastle.tls.TlsFatalAlert: 
> protocol_version(70)
>       at 
> bctls/org.bouncycastle.jsse.provider.ProvSSLEngine.unwrap(ProvSSLEngine.java:502)
>       at java.base/javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:679)
>       at 
> io.netty.handler.ssl.SslHandler$SslEngineType$3.unwrap(SslHandler.java:308)
>       at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1486)
>       at 
> io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1377)
>       at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1428)
>       at 
> io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:545)
>       at 
> io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:484)
>       ... 15 more
> Caused by: org.bouncycastle.tls.TlsFatalAlert: protocol_version(70)
>       at 
> bctls/org.bouncycastle.tls.AbstractTlsServer.getServerVersion(AbstractTlsServer.java:413)
>       at 
> bctls/org.bouncycastle.jsse.provider.ProvTlsServer.getServerVersion(ProvTlsServer.java:767)
>       at 
> bctls/org.bouncycastle.tls.TlsServerProtocol.generateServerHello(TlsServerProtocol.java:508)
>       at 
> bctls/org.bouncycastle.tls.TlsServerProtocol.handleHandshakeMessage(TlsServerProtocol.java:1058)
>       at 
> bctls/org.bouncycastle.tls.TlsProtocol.processHandshakeQueue(TlsProtocol.java:715)
>       at 
> bctls/org.bouncycastle.tls.TlsProtocol.processRecord(TlsProtocol.java:591)
>       at 
> bctls/org.bouncycastle.tls.RecordStream.readFullRecord(RecordStream.java:209)
>       at 
> bctls/org.bouncycastle.tls.TlsProtocol.safeReadFullRecord(TlsProtocol.java:926)
>       at 
> bctls/org.bouncycastle.tls.TlsProtocol.offerInput(TlsProtocol.java:1368)
>       at 
> bctls/org.bouncycastle.jsse.provider.ProvSSLEngine.unwrap(ProvSSLEngine.java:486){noformat}
>  
> This is because on client side in FIPS mode we create the SSLContext with 
> TLSv1.2 but the server uses TLSv1.3 and they cannot negotiate a common 
> protocol.
> If we'd like to have a SSLContext which supports multiple TLS versions on the 
> client side, we should call SSLContext.getProtocol("TLS") instead of what we 
> have right now:
> {code:java}
>         String defaultTlsProtocol = defaultTlsProtocol(config);
>         String protocol = config.getProperty(sslProtocolProperty, 
> defaultTlsProtocol);
>         try {
>             SSLContext sslContext = SSLContext.getInstance(protocol);
>             sslContext.init(keyManagers, trustManagers, null);
>             return new SSLContextAndOptions(this, config, sslContext);
>         } catch (NoSuchAlgorithmException | KeyManagementException 
> sslContextInitException) {
>             throw new SSLContextException(sslContextInitException);
>         }
> {code}
>  
> Note: This only affects branch-3.8 because in newer branches the 
> Netty-related code was refactored  
> https://issues.apache.org/jira/browse/ZOOKEEPER-4622 so this issue does not 
> come on 3.9.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to