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);
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
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:
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 (eg 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.
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:
JVM argument:
-Dhttps.protocols=TLSv1.2,TLSv1.1,TLSv1
Or set the same property from Java code:
System.setProperty("https.protocols", "TLSv1.2,TLSv1.1,TLSv1");
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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.