用Java SSLContextImpl $ TLS10Context随机“peer not authenticated”例外

使用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(); 

但是,如果我以这种方式创buildSSL上下文,则在我提到的任何Java版本上都没有连接失败:

 SSLSocketFactory factory = (SSLSocketFactory)SSLSocketFactory.getDefault(); 

第一种方法使用SSLContextImpl$TLS10Context和更高版本使用SSLContextImpl$DefaultSSLContext 。 看看代码,我没有看到会导致exception发生的任何差异。

为什么我会得到失败,使用getDefault()调用的优点/缺点是什么?

注:exception首先使用Apache HttpClient(版本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 

另一个信息是,如果closures代理服务器上的Diffie-Hellman,则不会发生错误。

根据症状判断,我这是与使用TLS错误启动的浏览器相关的,这是Google引入的一种客户端技巧,用于减lessTLS中的来回 :

虚假启动在很大程度上由浏览器控制,并通过将官方SSL规范中描述的数据的两个往返传递减less为单个往返传递而工作。 它通过指示客户端在一次调度中发送Finished和第一个ApplicationData消息,而不是将它们放在两个不同的包中,并在从服务器获得确认之后发送第二个包。

Googlebuild议将False Start作为官方标准,以使SSL更适合那些目前觉得太昂贵的网站。 通过缩短协商encryption密钥的握手和保护最终用户和网站之间数据传输所需的其他variables,False Start旨在降低很多人认为使用该协议所带来的性能损失。

从Mozilla Firefox中提出的相关问题 :(重点介绍我的)

到目前为止,已知存在当前或以前的假启动兼容性问题的不完整列表包括(AFAICT):F5,A10,Microsoft TMG,Cisco ASA,ServerIron ADX,ESET,NetNanny, Java的SSL服务器实现的一些configuration

javax.net.ssl.SSLPeerUnverifiedException仅仅因为http安全性而出现,所以您必须将您的连接configuration为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()); 

用这个。 我希望它能帮助你