当使用SSL连接到HAProxy服务器时,我会遇到随机出现的连接故障。我已经确认JDK 1.7.0_21和1.7.0_25版本会发生这些故障,但1.7.0_04或1.6.0_38版本不会发生这些故障。
是个例外
Exception in thread "main" javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
at sun.security.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:397)
at SSLTest2.main(SSLTest2.java:52)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
这些故障仅在使用TLS SSL上下文时发生,而不是在使用默认上下文时发生。以下代码在一个循环中运行一千次,在循环完成之前就会发生故障(大约2%的连接失败):
SSLContext sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(null, null, null);
SSLSocketFactory factory = sslcontext.getSocketFactory();
SSLSocket socket = (SSLSocket)factory.createSocket("myserver", 443);
//socket.startHandshake();
SSLSession session = socket.getSession();
session.getPeerCertificates();
socket.close();
然而,如果我以这种方式创建SSL上下文,我在我提到的任何Java版本上都没有连接故障:
SSLSocketFactory factory = (SSLSocketFactory)SSLSocketFactory.getDefault();
第一种方式使用SSLContextImpl$TLS10Context
,而后者使用SSLContextImpl$DefaultSSLContext
。查看代码,我没有发现任何会导致异常发生的差异。
为什么会出现故障?使用getDefault()
调用的优点/缺点是什么?
注意:异常首先是使用ApacheHttpClient(版本4)看到的。此代码是复制HttpClient问题的最小子集。
这是我在添加-Djavax.net.debug=ssl
时看到的错误:
main, READ: TLSv1 Alert, length = 2
main, RECV TLSv1 ALERT: fatal, bad_record_mac
%% Invalidated: [Session-101, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA]
main, called closeSocket()
main, handling exception: javax.net.ssl.SSLException: Received fatal alert: bad_record_mac
main, IOException in getSession(): javax.net.ssl.SSLException: Received fatal alert: bad_record_mac
另一条信息是,如果我在代理服务器上关闭Diffie-Hellman,则不会发生错误。
根据症状判断,我猜这与浏览器使用TLS错误启动有关,这是谷歌为减少TLS中的来回而引入的客户端技巧:
False Start在很大程度上由浏览器控制,其工作原理是将官方SSL规范中描述的两次往返数据传递减少为一次往返数据。它通过指示客户端在一次调度中发送Finished和第一个ApplicationData消息来实现这一点,而不是将它们放在两个不同的包中,并在得到服务器的确认后才发送第二个。
谷歌提议将"虚假启动"作为一项官方标准,以使SSL更受目前价格太高而无法提供的网站的欢迎。通过缩短协商加密密钥和其他变量的握手,以保护最终用户和网站之间的数据传递,False Start旨在降低许多人认为使用该协议会带来的性能损失。
来自Mozilla Firefox中提出的相关问题:(强调我的)
到目前为止,已知当前或以前存在错误启动兼容性问题的产品的不完整列表包括(AFAICT):F5、A10、Microsoft TMG、Cisco ASA、ServerIron ADX、ESET、NetNanny、Java SSL服务器实现的一些配置。
javax.net.ssl.SSLPeerUnverifiedException的出现只是因为http安全性,您必须将连接配置为https,否则请遵循此代码。。
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, new TrustManager[]{tm}, null);
SSLSocketFactory ssf = new SSLSocketFactory(ctx);
ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
ClientConnectionManager ccm = client.getConnectionManager();
SchemeRegistry sr = ccm.getSchemeRegistry();
sr.register(new Scheme("https", ssf, 443));
return new DefaultHttpClient(ccm, client.getParams());
使用这个。我希望它能帮助你