问题描述
使用HTPPSURLConnection发送https请求,并且设置了HTPPSURLConnection.setDefaultHostnameVerifier和HTPPSURLConnection.setDefaultSSLsocketFactory,以便weblogic作为客户端时不检验服务端的域名和证书,但是发送请求依然报错。
weblogic发送https请求报错:
javax.net.ssl.SSLKeyException:Hostname verification failed: HostnameVerifier=weblogic.security.utils.SSLWLSHostnameVerifier, hostname=*.xxx.com.
问题的奇怪之处在于该程序我已经测试通过并且上线,但是在测试环境某次被未知修改后,针对证书颁发给含有*通配符的域名
无法请求成功。
原因分析:
问题的关键在于weblogic的主机名校验机制
作为客户端发起https请求时,weblogic服务器会对所请求的服务端做SSL的主机名验证,验证方法是对服务端的授权证书和https请求的URL进行比对,比对规则视配置项而定。这项配置在weblogic服务器的console→环境→服务器→配置→SSL页面上,展开“高级”(advanced)菜单中。查看其中的“主机名验证”(host name varify )选项,会发现这里的默认配置为“BEA 主机名验证器”(BEA host name varify )。这个默认配置会对授权证书和URL进行完全匹配的验证,也就是必须完全相同,才认为是验证通过,并给https请求放行。
本次系统中出问题的地方,就是因为授权证书中的地址是“*.xxx.com”(采用了通配符的写法),而实际访问的地址是“yyy.xxx.com”。在完全匹配的规则下,当然会验证失败。
但是我的程序中发送请求中明明设置了HTPPSURLConnection.setDefaultHostnameVerifier和HTPPSURLConnection.setDefaultSSLsocketFactory,为什么忽略主机名校验和证书校验的设置不生效呢?
这个地方就涉及到一个优先级的问题了,经过测试发现,因为这个程序中设置的函数是setDefaultXXX
,注意这里,因为是设置的缺省的处理方法,所以当weblogic设置了BEA 主机名验证器的情况下,这个方法实际就不会生效了,默认就使用了BEA 主机名验证器。
解决方案:
解决方法有多种,具体根据情况自行选择
1、在weblogic启动脚本中加入如下命令:
Dweblogic.security.SSL.ignoreHostnameVerification=true
2、在weblogic启动脚本中加入如下命令:
-Dweblogic.security.SSL.hostnameVerifier=examples.security.sslclient.NulledHostnameVerifier
用此设置更改主机名验证,类examples.security.sslclient.NulledHostnameVerifier是weblogic的一个实现,用于取消主机名验证;另可以指定自定义主机名验证器类名,该类必须实现weblogic.security.SSL.HostnameVerifier 接口。
3、设置布署主机的SSL选项主机认证为无。(这可能会导致安全问题)
4、我的情况比较特殊,不能使用上述三种方法来忽略主机名校验,主要是因为经过比对生产和测试程序,发现生产上可以正常运行的程序未配置以上三项。经过比对测试环境和生产环境的weblogic启动脚本,我发现生产上设置了-DUseSunHttpHandler=true
,在测试环境配置以后,主机名校验的问题解决。
这个脚本命令,因为百度不到详细说明,根据名称猜测,应该是设置以后会使得java程序中设置的主机名校验和证书校验生效,不使用weblogic的主机名校验机制。
其实这个地方还有一个简单的修改方法,就是将java程序中使用的HTPPSURLConnection.setDefaultHostnameVerifier和HTPPSURLConnection.setDefaultSSLsocketFactory替换为HTPPSURLConnection.setHostnameVerifier和HTPPSURLConnection.setSSLsocketFactory,这样子java中设置的校验规则的优先级也会提升,即使不设置-DUseSunHttpHandler=true
,也可以成功发起请求。