0

There seem to be a bunch of posts on similar issues, but I couldn't find anything that worked for me from what was out there.

I have code in Java 7 that uses HttpClient 4.3.5 to GET a specific url. This code works when I run locally via a mvn test or when deployed to a tomcat server, but fails with a SSL handshake exception when running on in tomcat on a production server. I will include below the code being used (please ignore that some of how the httpclient is set up is completely insecure, it was meant to handle any site and certificate even if it's set up incorrectly) and the stacktrace of the error with -Djavax.net.debug=all included in the tomcat runtime to get additional logging details. I have already implemented the Java Cryptography Unlimited Strength Jurisdiction Policy Extension and also added in bouncy castle, allowing my code to work for most sites. Currently it seems to work for just about all of them except for a single site that only errors on the production server but works fine for me locally.

My Code:

Security.insertProviderAt(new BouncyCastleProvider(), 1);
KeyStore trustStore  = KeyStore.getInstance(KeyStore.getDefaultType());
SSLContextBuilder sslBuilder = new SSLContextBuilder();
sslBuilder.loadTrustMaterial(trustStore, new TrustStrategy() {
   @Override
   public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
      return true;
   }
});

SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslBuilder.build(), new String[] { "TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3"}, null, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
PlainConnectionSocketFactory pcsf = new PlainConnectionSocketFactory();
Registry socketFactoryRegistry = RegistryBuilder.create().register("https", sslsf).register("http", pcsf).build();
multiConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
multiConnectionManager.setMaxTotal(100);
multiConnectionManager.setDefaultMaxPerRoute(10);
httpClientBuilder.setConnectionManager(multiConnectionManager);
this.httpClient = httpClientBuilder.disableContentCompression().setSSLSocketFactory(sslsf).build();

HttpGet call = new HttpGet(url);
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().setCircularRedirectsAllowed(true).setMaxRedirects(5).setCookieSpec(CookieSpecs.IGNORE_COOKIES).build());
         try {
            getResponse = httpClient.execute(call);
         } 
         catch (Exception ex) {}
   } 
   catch (Exception ex) {}

Stacktrace:

trigger seeding of SecureRandom
done seeding SecureRandom
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_128_CBC_SHA256
Allow unsafe renegotiation: false
Allow legacy hello messages: true
Is initial handshake: true
Is secure renegotiation: false
Ignoring disabled protocol: SSLv3
%% No cached client session
*** ClientHello, TLSv1.2
RandomCookie:  GMT: 1470874697 bytes = { 159, 94, 240, 233, 124, 91, 106, 83, 249, 126, 156, 56, 200, 67, 114, 18, 205, 36, 55, 140, 229, 223, 66, 190, 204, 226, 223, 90 }
Session ID:  {}
Cipher Suites: [TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
Compression Methods:  { 0 }
Extension elliptic_curves, curve names: {secp256r1, sect163k1, sect163r2, secp192r1, secp224r1, sect233k1, sect233r1, sect283k1, sect283r1, secp384r1, sect409k1, sect409r1, secp521r1, sect571k1, sect571r1, secp160k1, secp160r1, secp160r2, sect163r1, secp192k1, sect193r1, sect193r2, secp224k1, sect239k1, secp256k1}
Extension ec_point_formats, formats: [uncompressed]
Extension signature_algorithms, signature_algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA224withECDSA, SHA224withRSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA, MD5withRSA
***
[write] MD5 and SHA1 hashes:  len = 181
0000: 01 00 00 B1 03 03 58 AC   C4 49 9F 5E F0 E9 7C 5B  ......X..I.^...[
0010: 6A 53 F9 7E 9C 38 C8 43   72 12 CD 24 37 8C E5 DF  jS...8.Cr..$7...
0020: 42 BE CC E2 DF 5A 00 00   2C C0 0A C0 14 00 35 C0  B....Z..,.....5.
0030: 05 C0 0F 00 39 00 38 C0   09 C0 13 00 2F C0 04 C0  ....9.8...../...
0040: 0E 00 33 00 32 C0 08 C0   12 00 0A C0 03 C0 0D 00  ..3.2...........
0050: 16 00 13 00 FF 01 00 00   5C 00 0A 00 34 00 32 00  ........\...4.2.
0060: 17 00 01 00 03 00 13 00   15 00 06 00 07 00 09 00  ................
0070: 0A 00 18 00 0B 00 0C 00   19 00 0D 00 0E 00 0F 00  ................
0080: 10 00 11 00 02 00 12 00   04 00 05 00 14 00 08 00  ................
0090: 16 00 0B 00 02 01 00 00   0D 00 1A 00 18 06 03 06  ................
00A0: 01 05 03 05 01 04 03 04   01 03 03 03 01 02 03 02  ................
00B0: 01 02 02 01 01                                     .....
ActiveMQ Session Task-2, WRITE: TLSv1.2 Handshake, length = 181
[Raw write]: length = 186
0000: 16 03 03 00 B5 01 00 00   B1 03 03 58 AC C4 49 9F  ...........X..I.
0010: 5E F0 E9 7C 5B 6A 53 F9   7E 9C 38 C8 43 72 12 CD  ^...[jS...8.Cr..
0020: 24 37 8C E5 DF 42 BE CC   E2 DF 5A 00 00 2C C0 0A  $7...B....Z..,..
0030: C0 14 00 35 C0 05 C0 0F   00 39 00 38 C0 09 C0 13  ...5.....9.8....
0040: 00 2F C0 04 C0 0E 00 33   00 32 C0 08 C0 12 00 0A  ./.....3.2......
0050: C0 03 C0 0D 00 16 00 13   00 FF 01 00 00 5C 00 0A  .............\..
0060: 00 34 00 32 00 17 00 01   00 03 00 13 00 15 00 06  .4.2............
0070: 00 07 00 09 00 0A 00 18   00 0B 00 0C 00 19 00 0D  ................
0080: 00 0E 00 0F 00 10 00 11   00 02 00 12 00 04 00 05  ................
0090: 00 14 00 08 00 16 00 0B   00 02 01 00 00 0D 00 1A  ................
00A0: 00 18 06 03 06 01 05 03   05 01 04 03 04 01 03 03  ................
00B0: 03 01 02 03 02 01 02 02   01 01                    ..........
[Raw read]: length = 5
0000: 15 03 03 00 02                                     .....
[Raw read]: length = 2
0000: 02 28                                              .(
ActiveMQ Session Task-2, READ: TLSv1.2 Alert, length = 2
ActiveMQ Session Task-2, RECV TLSv1 ALERT:  fatal, handshake_failure
ActiveMQ Session Task-2, called closeSocket()
ActiveMQ Session Task-2, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

Any help someone can provide to troubleshoot why this handshake error is happening and why it is only happening on the production server (linux box running the java code via tomcat) and works fine locally for me (mac running unit tests via mvn or java code via tomcat) would be most helpful.

Ben
  • 73
  • 1
  • 1
  • 7
  • The server doesn't like your hello; there are a huge number of possibilities but an **obvious possibilty is no ServerNameIndication** aka SNI. Check your working (local) case to see if you are sending SNI; if so but prod is not, recheck exactly what HttpClient you are running both places (especially if more than one might be present). Maybe also check Java versions; I don't remember any change to this within 7, but I could easily have forgotten. – dave_thompson_085 Feb 22 '17 at 07:43
  • @dave_thompson_085 I have already verified that the same version of java is running on both. I will attempt to verify that the same version of HttpClient is being used and isn't being overridden by tomcat or something, and lastly how do I verify if I'm sending SNI on my local case? The log looks almost identical to the one I posted above except instead of failing it succeeds and continues on. I can include the full log for the success on my local as well if it would be helpful for troubleshooting. – Ben Feb 22 '17 at 08:26
  • If SNI is being sent it will appear in the trace for the ClientHello as `Extension server_name, ...` -- see http://stackoverflow.com/questions/32341729/apache-http-client-defaults-dont-work-with-sni for an example (by someone who had exactly the problem that an undesired old version of httpclient was being used). – dave_thompson_085 Feb 22 '17 at 14:08
  • @dave_thompson_085 Yes I see that in the stacktrace for the working version on my local and it's not present in the stacktrace for my not working version on the server so maybe that's the issue. Is there an easy way to fix that from the code or tomcat server configuration? – Ben Feb 22 '17 at 18:12
  • @dave_thompson_085 I verified that the website in question does have SNI enabled, but what I'm still confused on is why my local handles it correctly but the server does not. Any idea why that might be? – Ben Feb 22 '17 at 20:13

1 Answers1

0

Ok finally figured this out. I fully credit dave_thompson_085 for pointing me in the right direction. It was a SNI issue.

As it turns out since I was using HttpClient 4.3.5 SNI was enabled by default in my local environment and tests, but in tomcat it was either pulling in an old library or had some other config set up that was causing it to be disabled in the production servers. Thus simply setting the system property:

System.setProperty("jsse.enableSNIExtension", "true");

Before the HttpClient libraries loaded in the code and then redeploying to tomcat on the production servers completely fixed the problem.

Ben
  • 73
  • 1
  • 1
  • 7