Random "peer not authenticated" exceptions with Java SSLContextImpl$TLS10Context
I get connection failures that appear randomly when connecting to an HAProxy server using SSL. I have confirmed that these failures happen on JDK versions 1.7.0_21 and 1.7.0_25 but not with 1.7.0_04 or with 1.6.0_38.
The exception is
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)
These failures only happen when using the TLS SSL context and not with the default context. The following code is run in a loop a thousand times and failures happen before the loop completes (about 2% of the connections fail):
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();
If, however, I create the SSL context this way I have no connections failures on any of the Java versions I mentioned:
SSLSocketFactory factory = (SSLSocketFactory)SSLSocketFactory.getDefault();
The first way uses SSLContextImpl$TLS10Context
and the later uses SSLContextImpl$DefaultSSLContext
. Looking at the code, I don't see any differences that would cause the exception to occur.
Why would I be getting the failures and what are the advantages/disadvantages of using the getDefault()
call?
Note: The exceptions were first seen using the Apache HttpClient (version 4). This code is the smallest subset that reproduces the problem seen with HttpClient.
Here's the error I see when adding -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
Another piece of information is that the errors do not occur if I turn off Diffie-Hellman on the proxy server.
Solution 1:
Judging by the symptoms, I'm guessing this is related to browsers using TLS false start, which is a client-side trick Google introduced to reduce the back-and-forth in TLS:
False Start is largely controlled by the browser and works by reducing the two round-trip passes of data described in official SSL specifications to a single round-trip pass. It did this by instructing the client to send Finished and first ApplicationData messages in a single dispatch rather than putting them in two distinct packages and sending the second only after getting confirmation from the server.
Google proposed False Start as an official standard to make SSL more palatable to websites that currently find it too expensive to offer. By abbreviating the handshake that negotiates the encryption key and other variables needed to protect data passing between the end user and website, False Start was intended to lower the performance penalty that many say comes from using the protocol.
From the relevant issue raised in Mozilla Firefox: (emphasis mine)
So far, an incomplete list of products that are known to have current or previous compatibility problems with False Start include (AFAICT): F5, A10, Microsoft TMG, Cisco ASA, ServerIron ADX, ESET, NetNanny, some configurations of Java's SSL server implementation.