简体   繁体   English

RestTemplate GET Request 中的 handshake_failure 在浏览器中工作

[英]handshake_failure in RestTemplate GET Request which works in browser

I'm failing to send simple GET request to 3rd party https URL which works fine in browser:我无法向第 3 方 https URL 发送简单的 GET 请求,这在浏览器中运行良好:

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没有证书,我正在使用 Java 8,尝试添加-Djsse.enableSNIExtension=false -Dhttps.protocols=TLSv1.2,TLSv1.1,TLSv1的解决方案

Added same headers as browser sends ( Accept and User-Agent ), but no luck添加了与浏览器发送相同的标题( AcceptUser-Agent ),但没有运气

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站点使用 cloudflare 服务

Also curl is working by putting full url, from verbose output it uses Server certificate with:此外,curl 正在通过将完整的 url 从冗长的 output 中放入其中使用服务器证书来工作:

SSL connection using TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384

Tried also with configuration skip SSL certificate verification with same output:还尝试使用相同的 output 进行配置跳过 SSL 证书验证

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

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

Or also with NoopHostnameVerifier :或者也可以使用NoopHostnameVerifier

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

curl -v results Server certificate: curl -v 结果服务器证书:

*       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 :我按照@Walk 的建议将证书添加到 java :

 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.我看到证书在浏览器中加载,它在 JMeter 中工作,但使用 restTemplate 或 apache HTTPClient 仍然失败并出现同样的错误。

I'm using Java 8 update 151.我正在使用 Java 8 更新 151。

Tried solution from @rmunge answer to add BouncyCastleProvider, but still same error尝试从@rmunge 回答添加 BouncyCastleProvider 的解决方案,但仍然出现相同的错误

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根本原因很可能是缺少对基于 EC 的最新密码套件的支持,例如TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384

Elliptical Curve Cryptography (ECC) is implemented by the SunEC provider.椭圆曲线密码术 (ECC) 由SunEC提供商实施。 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 SunEC 必须在 jre/lib/security/java.security 中列出,并且 jdk.tls.disabledAlgorithms 的值不能包含 ECDHE
  • The native library libsunce.so / sunce.dll has to be available in jre/lib (Linux, Mac) or jre/bin (Windows) folder本机库 libsunce.so / sunce.dll 必须在 jre/lib(Linux、Mac)或 jre/bin(Windows)文件夹中可用
  • Ensure that the JDK JCE framework uses the unlimited policy.确保 JDK JCE 框架使用无限制策略。 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.由于 Java 8u161 这是新的默认值,因此对于旧版本,您可能必须安装 JCE 无限强度管辖策略文件和/或在 java.security 文件中进行更改。 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 ).如果本机库不可用,则提供程序仍然有效,但仅提供基于 ECC 的密码子集(请参阅SunEC.java源代码中的注释)。 It seems that some linux distributions explicitely removed the native library or disabled the provider by default (eg RedHat , Amazon Linux ).似乎某些 linux 发行版在默认情况下明确删除了本机库或禁用了提供程序(例如RedHatAmazon 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.因此,如果该库不应该是您的 JRE 的一部分,请更新到最新的 package 版本或直接下载并安装最新的 OpenJDK 8 版本 - 也可以选择从下载中复制本机库 - 例如,请参见此处

Another option would be to use a third-party cryptography provider like Bouncy Castle which has its own Provider for ECC.另一种选择是使用第三方加密提供商,如 Bouncy Castle,它有自己的 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.....您能否指定您使用的是哪个版本的 Java... 如果 Java 7 那么这将为您完成工作.....

I can see that your client resolves to TLSv1.我可以看到您的客户端解析为 TLSv1。 From openssl output that your server does not support TLSv1.openssl output 得知您的服务器不支持 TLSv1。

TLS ver. TLS 版本。 1.1 and 1.2 are disabled in Java 7 by default. Java 7 中默认禁用 1.1 和 1.2。

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.尽管 Java SE 7 版本中的 SunJSSE 支持 TLS 1.1 和 TLS 1.2,但默认情况下这两个版本均未启用客户端连接。 Some servers do not implement forward compatibility correctly and refuse to talk to TLS 1.1 or TLS 1.2 clients.一些服务器没有正确实现前向兼容性并拒绝与 TLS 1.1 或 TLS 1.2 客户端通信。 For interoperability, SunJSSE does not enable TLS 1.1 or TLS 1.2 by default for client connections.对于互操作性,SunJSSE 默认情况下不为客户端连接启用 TLS 1.1 或 TLS 1.2。

Enable TLSv1.1 and TLSv1.2 either by:通过以下方式启用 TLSv1.1 和 TLSv1.2:

  1. JVM argument: JVM 参数:

     -Dhttps.protocols=TLSv1.2,TLSv1.1,TLSv1
  2. Or set the same property from Java code:或者从 Java 代码中设置相同的属性:

     System.setProperty("https.protocols", "TLSv1.2,TLSv1.1,TLSv1");
  3. Or install JCE Unlimited Strength policy files for Java 7 .或者为 Java 7 安装 JCE Unlimited Strength 策略文件 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.我不能 100% 确定这一步是否能解决问题,尽管安装 JCE 总是值得的,同时它允许 JVM 使用现有算法的更强大版本。

Note : Order of protocols changed from better to worse (TLS ver. 1.2 to 1) in options 1 and 2.注意:选项 1 和 2 中的协议顺序从好到坏(TLS 版本 1.2 到 1)发生了变化。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM