I am using Apache HttpClient 4.3 to interact with the API of hubic.com. My minimal reproducable example is just a one liner:
<
It looks like you are not using java 1.8
In Java 1.8 the default TLS protocol version is v1.2, whereas in Java 1.6,1.7 default is TLS1.0. As you can see in logs ClientHello, TLSv1.2 it means you are either using java 1.6 or 1.7 now you can either switch to 1.8 or Just add
static { System.setProperty("https.protocols", "TLSv1.2"); }
in your application class before main class
keeping you current java version
.. hope this helps !!
I have also come across this issue. Certain sites are not accessible, throwing the mysterious: javax.net.ssl.SSLException: Received fatal alert: protocol_version
I have tried to mend it, following some of the hints given here, but without success. At one point the usual thought “it used to work before..”, really started nagging me.
Initially I had assumed something had changed in the site I tried to connect to. However, as this is a legacy kind of site, it wasn’t so likely. So, what else could it be..? At one point I thought “Java version”. Unlikely, but maybe worth a try.
As it is, I can get the content from previously error-throwing sites, if I compile my code with SDK 1.6.0_25. Newer versions gives me problems: 1.7.0_51, and 1.8.0.
I have boiled it down to the smallest piece possible, if effect reproducing the one-liner initially posted by “Yankee” above. I have used the same line, including the same URL.
Compiling with SDK 1.6 it works.
I am not sufficiently skilled in Java and SSL to understand the root issue behind this. But, someone ought to be able to put the code side by side, and track it down..
This may save someone a few wasted hours.
I see from the debug that Java does a TLS1.2 handshake instead of the more common SSLv23 handshake:
main, WRITE: TLSv1.2 Handshake, length = 225
[Raw write]: length = 230
0000: 16 03 03 00
^^^^^ - TLS1.2 (SSL3.3)
The server itself can do only TLS1.0 and fails to just announce this older version. I'm not familiar with Java, but you either need set the protocol version to TLSv1 or SSLv23 to speak with this server.
The following code would set only TLSv1 with Apache HttpClient:
HttpClientBuilder
.create()
.setSslcontext(SSLContexts.custom().useProtocol("TLSv1").build())
.build()
.execute(new HttpGet("https://hubic.com"));
EDIT to include question from comment:
If you say that the server "fails to just announce this older version", does that mean that the server is misconfigured? In this case, why don't Firefox&Chromium have any problems?
If the client announces TLS1.2 in the ClientHello and the server can do only TLS1.0 it should announce this, i.e. reply with TLS1.0. The client can then close the connection if TLS1.0 is not good enough or continue with TLS1.0. But, in this case the server just told the client that it does not like this version and closed the connection. Firefox and others instead do a SSLv23 announcement, where they do a TLS1.0 handshake but also announce the best protocol version they support. This usually works good for older servers starting from SSL3.0 but also for newer servers.
You can check the behavior of the server with a recent Perl/IO::Socket::SSL and this script.
$ perl analyze-ssl.pl hubic.com
-- hubic.com port 443
* maximum SSL version : TLSv1 (SSLv23)
* supported SSL versions with handshake used and preferred cipher(s):
* handshake protocols ciphers
* SSLv23 TLSv1 AES256-SHA
* TLSv1_2 FAILED: SSL connect attempt failed error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number
* TLSv1_1 FAILED: SSL connect attempt failed error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number
* TLSv1 TLSv1 AES256-SHA
* SSLv3 FAILED: SSL connect attempt failed because of handshake problems error:1409442E:SSL routines:SSL3_READ_BYTES:tlsv1 alert protocol version
Here you can see, that the hosts supports SSLv23 and TLSv1 handshakes, but not the used TLSv1_2 handshake.
HttpClient can take an SSLContext for its SSLConnectionSocketFactory.
You can set the appropriate TLS version in the properties for the SSLContext
with the getInstance
static method.
Something like SSLContext context = SSLContext.getInstance("TLSv1");
If you need to force just one protocol (as getInstance
can return one that supports multiple protocols), you can use the setEnabledProtocols
method (which takes a String[]
) on the context
you retrieved using getInstance
.