3
votes

I'm failing to send simple GET request to 3rd party https URL which works fine in browser:

org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://...": Received fatal alert: handshake_failure; nested exception is javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:673)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:635)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:556)

I try follow related answers, but I didn't find solution.

There's no certificate and I'm using Java 8, tried solutions as adding -Djsse.enableSNIExtension=false -Dhttps.protocols=TLSv1.2,TLSv1.1,TLSv1

Added same headers as browser sends (Accept and User-Agent) , but no luck

Code

UriBuilder uriBuilder = UriBuilder.fromUri(URLDecoder.decode(URL, "UTF-8"));
HttpHeaders headers = new HttpHeaders();
headers.add("Accept", "text/html,application/xhtml+xml,application/xml");
headers.add(HttpHeaders.USER_AGENT, "Mozilla...");
HttpEntity<?> entity = new HttpEntity<Object>(headers);
ResponseEntity<ResponseVO> response = restTemplate.exchange(uriBuilder.build(), 
       HttpMethod.GET, entity, ResponseVO.class);
  • Site uses cloudflare services

Also curl is working by putting full url, from verbose output it uses Server certificate with:

SSL connection using TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384

Tried also with configuration skip SSL certificate verification with same output:

TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;

SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom() .loadTrustMaterial(null, acceptingTrustStrategy) .build();

Or also with NoopHostnameVerifier:

CloseableHttpClient httpClient = HttpClients.custom() .setSSLHostnameVerifier(new NoopHostnameVerifier()) .build(); HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); requestFactory.setHttpClient(httpClient);

curl -v results Server certificate:

*       subject: CN=*.site.com,OU=Domain Control Validated
*       start date: May 24 08:13:23 2019 GMT
*       expire date: May 24 08:13:23 2021 GMT
*       common name: *.site.com
*       issuer: CN=Go Daddy Secure Certificate Authority - G2,OU=http://certs.godaddy.com/repository/,O="GoDaddy.com, Inc.",L=Scottsdale,ST=Arizona,C=US

EDIT

I added certificate to java as @Walk suggested:

sudo keytool -importcert -file filename.cer -alias randomaliasname -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit 

Certificate was added to keystore

I see certificate loaded as in browser, it works in JMeter, but still failed with same error using restTemplate or apache HTTPClient.

I'm using Java 8 update 151.

Tried solution from @rmunge answer to add BouncyCastleProvider, but still same error

security.provider.6=org.bouncycastle.jce.provider.BouncyCastleProvider
2
Can you do the request using cURL or similar? Maybe your browser has a different trust-store than Java or something like that.Smutje
@Smutje curl is working by putting full urluser7294900
Is certificate check is enabled?Nish
@Nish it happens also with default restTemplate as new RestTemplate()user7294900
I believe @Smutje can be right. Perhaps your java trust-store does not have the server CA certificate, you can try to add it to your java trust-store, that would be my first try.Henrique Droog

2 Answers

2
votes

The root cause is most likely missing support for latest EC-based cipher suites like TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384

Elliptical Curve Cryptography (ECC) is implemented by the SunEC provider. To be fully functional the following is required:

  • SunEC has to be listed in jre/lib/security/java.security and the value of jdk.tls.disabledAlgorithms must not contain ECDHE
  • The native library libsunce.so / sunce.dll has to be available in jre/lib (Linux, Mac) or jre/bin (Windows) folder
  • Ensure that the JDK JCE framework uses the unlimited policy. Since Java 8u161 this is the new default, for older versions you may have to install the JCE Unlimited Strength Jurisdiction Policy Files and / or make changes in the java.security file. See here for further instructions.

If the native library is not available the provider still works but provides only a subset of ECC-based ciphers (see comments in source of SunEC.java). It seems that some linux distributions explicitely removed the native library or disabled the provider by default (e.g. RedHat, Amazon Linux). So if the library should not be part of your JRE, update to the latest package version or directly download and install latest OpenJDK 8 version - simply copying the native lib from the download could also be an option - see here for example.

Another option would be to use a third-party cryptography provider like Bouncy Castle which has its own Provider for ECC. For instruction see this question and its accepted answer.

1
votes

Can you please specify which version of Java you are using!!! If Java 7 then this will do the job for you.....

I can see that your client resolves to TLSv1. From openssl output that your server does not support TLSv1.

TLS ver. 1.1 and 1.2 are disabled in Java 7 by default.

Although SunJSSE in the Java SE 7 release supports TLS 1.1 and TLS 1.2, neither version is enabled by default for client connections. Some servers do not implement forward compatibility correctly and refuse to talk to TLS 1.1 or TLS 1.2 clients. For interoperability, SunJSSE does not enable TLS 1.1 or TLS 1.2 by default for client connections.

Enable TLSv1.1 and TLSv1.2 either by:

  1. JVM argument:

    -Dhttps.protocols=TLSv1.2,TLSv1.1,TLSv1
    
  2. Or set the same property from Java code:

    System.setProperty("https.protocols", "TLSv1.2,TLSv1.1,TLSv1");
    
  3. Or install JCE Unlimited Strength policy files for Java 7. I am not 100% sure if this single step would solve the problem although it is always worth to install JCE while it allows JVM to use stronger versions of existing algorithms.

Note: Order of protocols changed from better to worse (TLS ver. 1.2 to 1) in options 1 and 2.