问题
升级 JDK 17 后,一个自定义请求,提示 The server selected protocol version TLS10 is not accepted的异常,这个问题,在之前应该是碰到过的,所以有记录了对应的链接,https://cloud.tencent.com/developer/article/2127522。但这次在客户那部署,虽然调整了对应的参数,但依然还是请求失败。趁着周末重新验证了这个 TLSv1 的请求。
处理
解封 TLSv1
因为 TLSv1,TLSv1.1 有已知的安全漏洞,所以在高版本的 JDK里面默认的禁用了 TLSv1,TLSv1.1 的安全算法。通过whereis 和 ll 等命令可以定位 JAVA_HOME 目录(也可以直接尝试 echo $JAVA_HOME 看看是否有配置对应的目录),接着定位到$JAVA_HOME/conf/security/java.security 文件。
找到 jdk.tls.disabledAlgorithms部分,旧内容如下:(不同JDK 版本可能略有不同)
1 | =SSLv3, TLSv1, TLSv1.1, RC4, DES, MD5withRSA, \ |
去除 TLSv1, TLSv1.1, ,修改如下:
1 | =SSLv3, RC4, DES, MD5withRSA, \ |
如果不想修改这个配置,也可以尝试的程序运行时指定参数。
1 | java -Djdk.tls.client.protocols="TLSv1,TLSv1.1,TLSv1.2,TLSv1.3" -Djdk.tls.server.protocols="TLSv1,TLSv1.1,TLSv1.2,TLSv1.3" -jar app.jar |
通常情况下,此时应该能正常使用,但发到现场后发现还是上面的错误。
模拟环境
从提示中我们可以看到,应该是对方的https 服务使用的是 TLSv1 版本的协议,于是修改自己的 nginx 服务强制指定ssl_protocols 为 TLSv1;
1 | ssl_protocols TLSv1; |
此时浏览器范围页面也是异常的。
此时通过程序测试,提示的异常虽然有些不同,但基本可以看出是同一个问题引起的了。
尝试切换 hutool 的 httputil
切换 httputil 后发现服务请求自定义证书,提示下面的错误。
经过一番查看源代码后,发现 hutool 支持设置一个全局参数HttpGlobalConfig.setTrustAnyHost(true),设置了全局参数之后发现,请求正常了。
为什么 httputils 不行呢?
一开始以为是创建的 SSLContext 不同,更换成 hutool 工具创建的 SSLContext SSLContextUtil.createTrustAnySSLContext 依然还是不行。于是对比了下 hutool 和 solon 的是请求默认的类库不同,hutool 默认使用的是httpclient4,solon默认使用的是okhttp。当把 hutool 的默认请求切换到 okhttp 时,虽然一样也设置了*setTrustAnyHost*(true),但请求一样是报错的。
接着跟踪到 hutool 中 httpclient4 里面的一个关键设置。
1 | private static SSLConnectionSocketFactory buildSocketFactory(SSLInfo sslInfo) { |
此时可以比较明确的确定是连接客户也需要设置的问题,于是找到 OkHttpClient 创建的位置,增加设置connectionSpecs:
solon 作者说按上述设置会导致 http 无法访问,同时还发现 okhttp 还提供了一些常量,换成如下写法兼容性更好。
1 | connectionSpecs(Arrays.asList(ConnectionSpec.COMPATIBLE_TLS, ConnectionSpec.CLEARTEXT)) |
临时处理
扩展 OkHttpUtilsFactory
1 | public class OkHttpUtilsFactoryExt extends OkHttpUtilsFactory { |
扩展 HttpSslSupplier
1 |
|
使用
如果指定了 HttpExtension的扩展为 Component,默认情况是会自动注册扩展的,测试时可能需要自己通过addExtension 的方式添加扩展。
1 | HttpConfiguration.setFactory(new OkHttpUtilsFactoryExt()); |
正式处理
等待 solon 3.5.0 版本的发布,之后切换 SSLContextUtil.createTrustAnySSLContext(),替换默认的SSLContext。