Java 8 从哪个版本开始禁用了 TLS 1.0 和 1.1?——一次 Netty SSL 握手失败的排查过程

该文章已生成可运行项目,

在一次使用 Netty 做服务端进行 TLS 通信开发的过程中,客户端与服务端握手总是失败。经过多番排查,最终发现问题出在了 TLS 协议版本的不兼容 —— Java 从某个版本开始默认禁用了 TLS 1.0 和 1.1。本文将围绕这个问题展开,帮助开发者理清 Java 中 TLS 协议版本的支持情况,以及如何合理配置以保证通信顺利进行。


📌 背景:一次 Netty SSL 握手失败

在实现服务端 SSLContext 初始化时,我们使用了如下代码:


typescript

代码解读

复制代码

private SSLEngine getSslServerEngine() { SSLEngine sslEngine = getServerContext().createSSLEngine(); sslEngine.setUseClientMode(SSLConfig.NEED_CLIENT_AUTH); sslEngine.setEnabledProtocols(new String[]{"TLSv1", "TLSv1.1", "TLSv1.2"}); sslEngine.setNeedClientAuth(SSLConfig.NEED_CLIENT_AUTH); return sslEngine; }

可以看到,为了兼容旧客户端,我们手动开启了 "TLSv1""TLSv1.1" 和 "TLSv1.2"

然而在实际运行过程中,客户端始终握手失败。服务端日志打印握手失败,客户端报错为 handshake_failure,甚至 protocol_version。经过深入排查,最终发现——Java 的某些版本默认禁用了 TLSv1 和 TLSv1.1,导致 SSL 引擎虽然设置了这些协议,却根本无法启用。


✅ 关键版本:Java 1.8.0_291

从 Java 8u291(即 1.8.0_291)开始,Oracle 和 OpenJDK 默认在安全策略中 禁用了 TLS 1.0 和 1.1

这是通过修改 java.security 文件中的如下配置实现的:


ini

代码解读

复制代码

jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, RC4, DES, MD5withRSA, \ DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL, \ include jdk.disabled.namedCurves


🔍 如何查看当前禁用状态?

先使用java -version查看小版本确定高于1.8.0_291,你可以在你的 JDK 安装目录中找到 lib/security/java.security 文件,查找 jdk.tls.disabledAlgorithms 这一项。


🔧 解决方案

✅ 推荐:只使用 TLSv1.2+

修改你的代码和服务端配置,只启用 TLS 1.2 或以上协议:


arduino

代码解读

复制代码

sslEngine.setEnabledProtocols(new String[]{"TLSv1.2"});

确保客户端也支持 TLSv1.2。


⚠️ 临时方案:修改默认安全配置(不推荐生产使用)

打开 java.security 文件,移除禁用项:


ini

代码解读

复制代码

jdk.tls.disabledAlgorithms=SSLv3, RC4, DES, ...

⚠️ 不建议直接修改 JDK 全局配置,容易影响其他系统组件。


💡 更安全的做法:使用自定义 java.security 配置文件

创建一个自定义配置文件,例如:


bash

代码解读

复制代码

./resources/conf/custom-java.security

内容为(只包含你要覆盖的项):


ini

代码解读

复制代码

jdk.tls.disabledAlgorithms=SSLv3, RC4, DES, ...

然后在启动 Java 应用时,指定该文件:


ini

代码解读

复制代码

-Djava.security.properties=./resources/conf/custom-java.security

✅ 这种方式不会影响全局 JDK 安装,是推荐的替代方案。

⚠️ 强烈建议仅在必要场景下使用此方法,并计划尽快升级使用 TLS 1.2 或 TLS 1.3。


💡 建议

  • 优先使用 TLS 1.2 或 TLS 1.3,尤其是在生产环境。
  • 检查你的服务端和客户端的协议支持情况,确保兼容。
  • 使用工具如 nmapopenssl s_client 等检查 TLS 支持情况。

🔚 总结

  • Java 从 1.8.0_291 起默认禁用了 TLSv1 和 TLSv1.1
  • 设置 SSLEngine.setEnabledProtocols(...) 不能绕过此禁用
  • 推荐升级所有客户端支持 TLSv1.2+
  • 如果必须兼容旧协议,使用自定义 java.security 文件 + 启动参数是最佳实践

这一改变对提高安全性至关重要。开发者需要了解这个变化对现有应用的影响,并尽快进行适配升级。

本文章已经生成可运行项目
Exception in thread "main" org.springframework.web.reactive.function.client.WebClientRequestException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target; nested exception is javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction.lambda$wrapException$9(ExchangeFunctions.java:141) Suppressed: The stacktrace has been enhanced by Reactor, refer to additional information below: Error has been observed at the following site(s): *__checkpoint ⇢ Request to POST https://n.i.td.com/v1/firm1/bat?token=default&id=default&user=default&model=V&hwVer=1.0&hwId=AGGGGGGGGGGEF4212UUUUUU083B3D1EA&oemId=9551DFDATTTTTTTTT0F0E70FXXXX38D&fwVer=&deviceType=C.LOCAL [DefaultWebClient] Original Stack Trace: at org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction.lambda$wrapException$9(ExchangeFunctions.java:141) at reactor.core.publisher.MonoErrorSupplied.subscribe(MonoErrorSupplied.java:55) at reactor.core.publisher.Mono.subscribe(Mono.java:4490) at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:103) at reactor.core.publisher.FluxPeek$PeekSubscriber.onError(FluxPeek.java:222) at reactor.core.publisher.FluxPeek$PeekSubscriber.onError(FluxPeek.java:222) at reactor.core.publisher.FluxPeek$PeekSubscriber.onError(FluxPeek.java:222) at reactor.core.publisher.MonoNext$NextSubscriber.onError(MonoNext.java:93) at reactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onError(MonoFlatMapMany.java:204) at reactor.core.publisher.SerializedSubscriber.onError(SerializedSubscriber.java:124) at reactor.core.publisher.FluxRetryWhen$RetryWhenMainSubscriber.whenError(FluxRetryWhen.java:225) at reactor.core.publisher.FluxRetryWhen$RetryWhenOtherSubscriber.onError(FluxRetryWhen.java:274) at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onError(FluxContextWrite.java:121) at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:415) at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onNext(FluxConcatMap.java:251) at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onNext(FluxContextWrite.java:107) at reactor.core.publisher.EmitterProcessor.drain(EmitterProcessor.java:537) at reactor.core.publisher.EmitterProcessor.tryEmitNext(EmitterProcessor.java:343) at reactor.core.publisher.SinkManySerialized.tryEmitNext(SinkManySerialized.java:100) at reactor.core.publisher.InternalManySink.emitNext(InternalManySink.java:27) at reactor.core.publisher.FluxRetryWhen$RetryWhenMainSubscriber.onError(FluxRetryWhen.java:190) at reactor.core.publisher.MonoCreate$DefaultMonoSink.error(MonoCreate.java:201) at reactor.netty.http.client.HttpClientConnect$MonoHttpConnect$ClientTransportSubscriber.onError(HttpClientConnect.java:307) at reactor.core.publisher.MonoCreate$DefaultMonoSink.error(MonoCreate.java:201) at reactor.netty.resources.DefaultPooledConnectionProvider$DisposableAcquire.onUncaughtException(DefaultPooledConnectionProvider.java:210) at reactor.netty.resources.DefaultPooledConnectionProvider$PooledConnection.onUncaughtException(DefaultPooledConnectionProvider.java:464) at reactor.netty.channel.ChannelOperationsHandler.exceptionCaught(ChannelOperationsHandler.java:147) at io.netty.channel.AbstractChannelHandlerContext.invokeExceptionCaught(AbstractChannelHandlerContext.java:346) at io.netty.channel.AbstractChannelHandlerContext.invokeExceptionCaught(AbstractChannelHandlerContext.java:325) at io.netty.channel.AbstractChannelHandlerContext.fireExceptionCaught(AbstractChannelHandlerContext.java:317) at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireExceptionCaught(CombinedChannelDuplexHandler.java:424) at io.netty.channel.ChannelHandlerAdapter.exceptionCaught(ChannelHandlerAdapter.java:92) at io.netty.channel.CombinedChannelDuplexHandler$1.fireExceptionCaught(CombinedChannelDuplexHandler.java:145) at io.netty.channel.ChannelInboundHandlerAdapter.exceptionCaught(ChannelInboundHandlerAdapter.java:143) at io.netty.channel.CombinedChannelDuplexHandler.exceptionCaught(CombinedChannelDuplexHandler.java:231) at io.netty.channel.AbstractChannelHandlerContext.invokeExceptionCaught(AbstractChannelHandlerContext.java:346) at io.netty.channel.AbstractChannelHandlerContext.invokeExceptionCaught(AbstractChannelHandlerContext.java:325) at io.netty.channel.AbstractChannelHandlerContext.fireExceptionCaught(AbstractChannelHandlerContext.java:317) at reactor.netty.tcp.SslProvider$SslReadHandler.userEventTriggered(SslProvider.java:850) at io.netty.channel.AbstractChannelHandlerContext.invokeUserEventTriggered(AbstractChannelHandlerContext.java:400) at io.netty.channel.AbstractChannelHandlerContext.invokeUserEventTriggered(AbstractChannelHandlerContext.java:376) at io.netty.channel.AbstractChannelHandlerContext.fireUserEventTriggered(AbstractChannelHandlerContext.java:368) at io.netty.handler.ssl.SslHandler.handleUnwrapThrowable(SslHandler.java:1260) at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1241) at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1285) at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:529) at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:468) at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:290) 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:1410) 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:919) at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166) at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788) at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724) at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562) at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) 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:750) Suppressed: java.lang.Exception: #block terminated with an error at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:99) at reactor.core.publisher.Mono.block(Mono.java:1742) at org.example.PostDemo.main(PostDemo.java:46) Caused by: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.ssl.Alert.createSSLException(Alert.java:131) at sun.security.ssl.TransportContext.fatal(TransportContext.java:331) at sun.security.ssl.TransportContext.fatal(TransportContext.java:274) at sun.security.ssl.TransportContext.fatal(TransportContext.java:269) at sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1356) at sun.security.ssl.CertificateMessage$T13CertificateConsumer.onConsumeCertificate(CertificateMessage.java:1231) at sun.security.ssl.CertificateMessage$T13CertificateConsumer.consume(CertificateMessage.java:1174) at sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:377) at sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:444) at sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:981) at sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:968) at java.security.AccessController.doPrivileged(Native Method) at sun.security.ssl.SSLEngineImpl$DelegatedTask.run(SSLEngineImpl.java:915) at io.netty.handler.ssl.SslHandler.runDelegatedTasks(SslHandler.java:1549) at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1395) at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1236) at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1285) at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:529) at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:468) at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:290) 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:1410) 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:919) at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166) at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788) at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724) at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562) at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) 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:750) Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:456) at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:323) at sun.security.validator.Validator.validate(Validator.java:271) at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:315) at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:278) at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:141) at sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1334) ... 31 more Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:148) at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:129) at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280) at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:451)
最新发布
12-03
io.netty.handler.codec.DecoderException: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:468) ~[netty-all-4.1.46.Final.jar:4.1.46.Final] at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276) ~[netty-all-4.1.46.Final.jar:4.1.46.Final] at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377) [netty-all-4.1.46.Final.jar:4.1.46.Final] at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363) [netty-all-4.1.46.Final.jar:4.1.46.Final] at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:355) [netty-all-4.1.46.Final.jar:4.1.46.Final] at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) [netty-all-4.1.46.Final.jar:4.1.46.Final] at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377) [netty-all-4.1.46.Final.jar:4.1.46.Final] at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363) [netty-all-4.1.46.Final.jar:4.1.46.Final] at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) [netty-all-4.1.46.Final.jar:4.1.46.Final] at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163) [netty-all-4.1.46.Final.jar:4.1.46.Final] at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714) [netty-all-4.1.46.Final.jar:4.1.46.Final] at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650) [netty-all-4.1.46.Final.jar:4.1.46.Final] at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576) [netty-all-4.1.46.Final.jar:4.1.46.Final] at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493) [netty-all-4.1.46.Final.jar:4.1.46.Final] at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989) [netty-all-4.1.46.Final.jar:4.1.46.Final] at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) [netty-all-4.1.46.Final.jar:4.1.46.Final] at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) [netty-all-4.1.46.Final.jar:4.1.46.Final] at java.lang.Thread.run(Thread.java:750) [na:1.8.0_432] Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure at sun.security.ssl.Alert.createSSLException(Alert.java:131) ~[na:1.8.0_432] at sun.security.ssl.Alert.createSSLException(Alert.java:117) ~[na:1.8.0_432] at sun.security.ssl.TransportContext.fatal(TransportContext.java:342) ~[na:1.8.0_432] at sun.security.ssl.Alert$AlertConsumer.consume(Alert.java:293) ~[na:1.8.0_432] at sun.security.ssl.TransportContext.dispatch(TransportContext.java:185) ~[na:1.8.0_432] at sun.security.ssl.SSLTransport.decode(SSLTransport.java:156) ~[na:1.8.0_432] at sun.security.ssl.SSLEngineImpl.decode(SSLEngineImpl.java:588) ~[na:1.8.0_432] at sun.security.ssl.SSLEngineImpl.readRecord(SSLEngineImpl.java:544) ~[na:1.8.0_432] at sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:411) ~[na:1.8.0_432] at sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:390) ~[na:1.8.0_432] at javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:629) ~[na:1.8.0_432] at io.netty.handler.ssl.SslHandler$SslEngineType$3.unwrap(SslHandler.java:281) ~[netty-all-4.1.46.Final.jar:4.1.46.Final] at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1338) ~[netty-all-4.1.46.Final.jar:4.1.46.Final] at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1233) ~[netty-all-4.1.46.Final.jar:4.1.46.Final] at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1280) ~[netty-all-4.1.46.Final.jar:4.1.46.Final] at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:498) ~[netty-all-4.1.46.Final.jar:4.1.46.Final] at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:437) ~[netty-all-4.1.46.Final.jar:4.1.46.Final] ... 17 common frames omitted
09-27
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值