【bugfix】https握手失败 javax.net.ssl.SSLException: Received fatal alert: internal_error

目录

1.背景

2.分析过程

2.1抓包

2.2 控制变量

3.原因

4.解决方案


1.背景

对接外部服务使用https协议,通过浏览器和curl 7.54.0、OpenSSL 1.1.1d  10 Sep 2019都能通,但是开发机上通过commons-httpclient 3.1包通过注册ssl协议进行socket通信时,报SSLException: Received fatal alert: internal_error异常。

curl -v https://xxxx.xxx/xx/xx

openssl s_client -connect xxxx.xxx:443

2.分析过程

2.1抓包

首先在开发机进行抓包,用root用户 执行tcpdump通过指定对端ip针对性抓包

tcpdump -i any -nn host xxx.xx.xx.xx -w /home/xxx/cap0311.cap

【bugfix】https握手失败 javax.net.ssl.SSLException: Received fatal alert: internal_error_第1张图片

【bugfix】https握手失败 javax.net.ssl.SSLException: Received fatal alert: internal_error_第2张图片

2.2 控制变量

为了确认不是jar包的原因,在办公机器写了demo,使用相同的类库,相同的实现,进行通信发现没有报错,再次进行抓办公机器上的包,对比发现多了几行扩展信息,包括server_name,由于curl的时候就发现对端的服务是nginx,nginx通常会有多个虚拟主机,如果不指定server_name,无法指定到对应虚拟主机处理,所以定位在这一点的问题

【bugfix】https握手失败 javax.net.ssl.SSLException: Received fatal alert: internal_error_第3张图片

Extension server_name, server_name: [type=host_name (0), value=xxx.cn]

办公debug发现在SSLSocketImpl.connect(host, timeout)后会将其serverNames加入url中的host,而开发机相同代码执行到这一步并没有塞入serverName。由于SSLSocketImpl是sun.security.ssl JDK实现,/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/jsse.jar!/sun/security/ssl/SSLSocketImpl.class可以发现是jre的类,由于是相同jar包、相同实现,所以可以排除是jar包版本问题,办公机器和开发机上的差异在于JDK版本,开发机器是JDK1.8.0_121,所以可能是版本问题

【bugfix】https握手失败 javax.net.ssl.SSLException: Received fatal alert: internal_error_第4张图片

【bugfix】https握手失败 javax.net.ssl.SSLException: Received fatal alert: internal_error_第5张图片

3.原因

服务端通过SNI(Server Name Indication)来区别虚拟主机,而开发机环境没有传输SNI扩展信息,引发服务端内部错误。

在某些场景中,需要获取ClientHello中的SNI字段来作为一个必要条件, 比如用NGINX stream对HTTPS流量做4层代理时。客户端ClientHello中没有携带SNI,则会造成一个通过代理握手失败的局面。

在利用NGINX stream做正向代理时,NGXIN服务器需要获取客户端想要访问的目的域名。利ngx_stream_ssl_preread_module模块在不解密的情况下拿到ClientHello报文中SNI才能实现代理的正常功能。

4.解决方案

升级JDK版本,从从jdk jdk1.8.0_121 升级到jdk1.8.0_212,开发机代码通信正常~

 

你可能感兴趣的:(java,通信)