I'm attempting to execute a get call to the website https://www.facebookbrand.com/ and in doing so I'm receiving the error (SSLException) Received fatal alert: protocol_version. The website in question is using TLSv1.2 and if I setup my ConnectionFactory to specifically support this protocol only it works fine, but I need it to work for every website and from my understanding hard coding it to TLSv1.2 would make it not work for other https websites that don't use TLSv1.2. I also tried including a list of protocols ("TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3", "SSLv2Hello") but then I get the error Unrecognized SSL message, plaintext connection. I don't care about how secure it is I just need to be able to connect to every website essentially and am trying to find the best way to do this. Below I have included my code.
Here's my code for setting up the httpClient etc:
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslBuilder.build(), SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
PlainConnectionSocketFactory pcsf = new PlainConnectionSocketFactory();
Registry socketFactoryRegistry = RegistryBuilder.create().register("https", sslsf).register("http", pcsf).build();
PoolingHttpClientConnectionManager multiConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
multiConnectionManager.setMaxTotal(100);
multiConnectionManager.setDefaultMaxPerRoute(10);
httpClientBuilder.setConnectionManager(multiConnectionManager);
this.httpClient = httpClientBuilder.disableContentCompression().setSSLSocketFactory(sslsf).build();
Here's the code for the get request and execution via httpClient:
HttpGet call = new HttpGet(url);
LinkPullerContentHandler handler = new LinkPullerContentHandler();
PageLinks pl = new PageLinks(url, 200, null);
HttpResponse getResponse;
try {
call.addHeader("User-Agent","Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0)");
call.addHeader("Accept","text/html");
call.setConfig(RequestConfig.custom().setCookieSpec(CookieSpecs.IGNORE_COOKIES).build());
try {
getResponse = httpClient.execute(call);
} catch (SocketTimeoutException ste) {
LOG.error(String.format("Socket timeout pulling URL %s; skipping",url));
pl.setHttpStatusCode(408);
return pl;
} catch (ConnectTimeoutException cte) {
LOG.warn(String.format("Connect timeout pulling URL %s; skipping", url));
pl.setHttpStatusCode(408);
return pl;
} catch (UnknownHostException uhe) {
LOG.warn("Unable to resolve host for URL {}; skipping", url);
pl.setHttpStatusCode(404);
return pl;
} catch (Exception e) {
LOG.error(String.format("Unable to load link %s: (%s) %s",
url,e.getClass().getSimpleName(),e.getMessage()),e);
pl.setHttpStatusCode(420);
return pl;
}
int code = getResponse.getStatusLine().getStatusCode();
In attempting to include specifically the TLSv1.2 protocol I modified the SSLConnectionSocketFactory setup code to this:
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslBuilder.build(),
new String[] { "TLSv1.2" }, null,
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
Which works for the website in question but I feel like it would fail on some others since it's hard coded to TLSv1.2. Alternatively here was how it looked when I tried including additional protocols:
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslBuilder.build(),
new String[] { "TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3", "SSLv2Hello"},
null, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
Which ends up getting the exception "Unrecognized SSL message, plaintext connection." I am also attempting to include a list of cipherSuites to see if this helps but I haven't had any success with it yet.
Any help someone can provide to help me figure out how to make this work while not sacrificing being able to get other sites would be awesome.
Thanks in advance
Don't use SSLv2Hello.
That site apparently doesn't support v2 format hello; even though it will successfully negotiate TLSv1.2 on a v3+ format hello, if I send (with openssl) v2 format supporting upgrade to TLSv1.2 (wire version 3.3), it responds with an HTML body (not even HTTP headers) in plaintext saying "Service unavailable" and "Site unavailable". That is of course not a valid response for either v2 protocol or v3+ protocols.
If you configure all protocols from SSLv3 through TLSv1.2 but not SSLv2Hello -- which is the default for Java8 anyway -- you will be able to negotiate with other sites that support anything down to SSLv3, assuming no other problem, like the site uses an invalid cert. (And excepting any site that implements only draft TLSv1.3 and nothing standard.) If there is any server still out there so old or lame it supports only SSLv2, Java can't actually communicate with it anyway; the only "benefit" of SSLv2Hello is that JSSE throws a specific exception about "SSLv2 not supported" rather than varied and often ambiguous comms errors that are harder to debug.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With