使用 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,则不会发生错误。