使用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()
调用有什么优缺点?注意:这些异常最初是使用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
另外一个信息是,如果我在代理服务器上关闭Diffie-Hellman,则不会发生错误。