JDK8编译JDK11运行时BouncyCastle ALPN协商失败问题咨询
问题说明
现有配置
- JDK安全提供者注册代码:
Security.addProvider(new BouncyCastleFipsProvider())
- gRPC客户端SSL上下文构建代码:
SslContextBuilder sslContextBuilder = SslContextBuilder.forClient() .sslProvider(SslProvider.JDK) .applicationProtocolConfig(new ApplicationProtocolConfig( ApplicationProtocolConfig.Protocol.ALPN, ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE, ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT, Collections.singletonList("h2")));
- 环境版本信息:
- gRPC版本:1.44(升级至1.46后问题仍存在)
- bc-fips.jar版本:1.0.2
- 编译JDK版本:JDK 1.8.0_291
- 运行JDK版本:Java 11.0.15
异常现象
SSL握手完成后抛出如下错误:
UNAVAILABLE: Failed ALPN negotiation: Unable to find compatible protocol
Channel Pipeline: [SslHandler#0, ProtocolNegotiators$ClientTlsHandler#0, WriteBufferingAndExceptionHandler#0, DefaultChannelPipeline$TailContext#0]
初步排查结果
已确认根因指向BouncyCastleFipsProvider的SSLParametersUtil类存在逻辑缺陷,会异常移除ALPN建议协议列表中的h2协议,升级gRPC版本无法修复该问题。
解决方案
按落地成本从低到高排序,可选择以下任意一种方案修复:
- 禁用BC FIPS的JSSE实现/调整注册优先级
问题触发的核心原因是BC FIPS 1.0.2版本的JSSE实现接管了SSL参数处理逻辑,存在ALPN协议过滤bug。两种调整方式都可解决:- 追加JVM启动参数
-Dorg.bouncycastle.fips.jsse.enable=false,禁用BC FIPS自带的JSSE实现,仅保留其提供的各类加密算法能力,SSL/TLS逻辑继续由JDK原生JSSE处理,无需修改业务代码。 - 若通过代码调整,不要将BC FIPS注册为最高优先级(位置1)的安全提供者,将其放在JDK原生JSSE提供者序列之后即可,避免BC的SSL逻辑被优先加载。
- 追加JVM启动参数
- 升级bc-fips版本到1.0.2.3及以上
1.0.2版本的bc-fips存在已知ALPN处理bug:逻辑中错误过滤了长度小于3字节的ALPN协议名,而h2长度为2字节,因此被意外移除。1.0.2.3及以上版本已经修复该判断逻辑,升级后无需调整其他配置即可正常协商h2协议。 - 切换SSL提供者为OPENSSL
若环境可引入netty-tcnative本地依赖,可将SSL实现从JDK切换为OpenSSL,完全绕过JDK层安全提供者的ALPN处理逻辑,同时能获得更好的SSL性能。修改SSL上下文配置:
该方案需要确保部署环境的系统架构、glibc版本与引入的netty-tcnative二进制包匹配。SslContextBuilder sslContextBuilder = SslContextBuilder.forClient() .sslProvider(SslProvider.OPENSSL) .applicationProtocolConfig(new ApplicationProtocolConfig( ApplicationProtocolConfig.Protocol.ALPN, ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE, ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT, Collections.singletonList("h2"))); - 强制覆写SSLEngine的ALPN参数(临时规避)
若暂时无法升级依赖、也无法调整BC配置,可添加SSL引擎定制器,在SSLEngine初始化完成后强制写入正确的ALPN协议列表,绕过BC的参数过滤逻辑:sslContextBuilder.sslEngineConfigurator(sslEngine -> { SSLParameters params = sslEngine.getSSLParameters(); params.setApplicationProtocols(new String[]{"h2"}); sslEngine.setSSLParameters(params); return sslEngine; });
内容的提问来源于stack exchange,提问作者yatir dagan
相关产品推荐
相关产品推荐

