39点博客

39点博客
像小蜜蜂一样生活

解决 javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated 问题

问题症状

最近把一部分服务迁移到用 rancher 管理的容器集群环境里面去了,感觉还是不错的,而且 rancher 里自带的负载均衡是 haproxy,配置上域名证书后由 haproxy 来负责 https 的处理,后端就不用配 https 了。服务跑起来之后,在日志中发现出现类似下面错误:

javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated

出现地方是在用 HttpClient 请求我们自己的 https 域名时报错,看起来是域名的证书没法验证通过。

问题初探

网上搜索了之后发现有很多相关的内容,但是很多都是因为访问自签名证书的域名而产生的问题,给出的方案都是在代码里取消了对域名证书的验证。而我们的域名签名是有效的,不应该会出现这种问题,我们也不需要为了解决自签名证书去取消验证,所以药不对症,得继续寻找答案。

本地调试

用 HttpClient 在本地写了简单的测试代码运行,发现同样报错,缩小了问题范围。关键点:设置 System.setProperty("javax.net.debug", "ssl"); 打开 ssl 的调试信息,可以获得相当多关键信息。在我的代码里,出错信息如下:

handling exception: javax.net.ssl.SSLHandshakeException:Remote host closed connection during handshake

网上搜索了之后逐渐发现了关键问题所在: 域名证书是正常的,但 SSL 握手失败,判断是 JDK 使用的 SSL 协议和加密算法与我们的 WebServer 不匹配。

SSL Server 兼容性检查

这里要祭出一个强力法器了 https://www.ssllabs.com/ssltest/ 这个工具可以对域名的证书做详细的检查,更实用的功能是可以反馈各种 User Agent 访问的可用性,把我们的域名丢进去检测一下,得分 A 还不错,再仔细看看下面的列表…jdk6 jdk7 都显示无法连接…简直是个大悲剧。

谜底揭晓

这里涉及到 HTTPS 的几个概念,Protocols 和 Cipher Suites。一般来说,在给网站或应用做 SSL 加密时,Protocol 有以下几种:TLS1.2、TLS1.1、TLS1.0、SSLv3、SSLv2。不同的协议下使用的 Cipher Suites 也有不同。SSLv3 和 SSLv2由于前两年被爆出的严重安全漏洞,目前已经被认为不适合使用了,稍微新一点的 webserver 默认都是关闭这两项的,关键在于 TLS1.0。通过上面那个检查 SSL 的网站发现我们的服务器(即 rancher 中的 haproxy),是只支持了 TLS1.2 和 TLS1.1 这两项的,实际上这也是现代服务器的推荐配置。

重点来了! jdk6 默认是只支持 TLS1.0 的,jdk7 支持 TLS1.1 和 TLS1.2 但是默认是不开启的,只有 jdk8 才默认选择最高的 TLS1.2。所以,只要是运行在 jdk6 和 jdk7 上,就有可能遇到这种问题。并且,我尝试了在 jdk7 中开启 TLS1.1 和 TLS1.2 的支持,但还是同样的错误,发现可能是由于我们的证书支持的 Cipher Suites 和 jdk7 中支持的对不上,还是验证失败。

解决方案

到这里怎么解决问题就比较清楚了,可以将部署了应用的 jetty 容器升级到 jdk8 的环境,继续使用 TLS1.2、TLS1.1 的配置组合即可。但是这样实际上还是放弃了对一部分旧 user agent 的支持,从上面那个检查的列表中来看,Android4.4 以下都是默认只支持 TLS1.0 的,所以出于更好的访问兼容性,还是打开对 TLS1.0 的支持比较好。

配置 Nginx支持 TLS1.0

修改nginx配置

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

ssl_ciphers AESGCM:ALL:!DH:!EXPORT:!RC4:+HIGH:!MEDIUM:!LOW:!aNULL:!eNULL;

ssl_prefer_server_ciphers on;


原创类文章未经允许请勿转载:39点博客 » 解决 javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated 问题

分享到: +More

评论 沙了个发

换个身份

取消评论