在RestTemplate的GET请求中,握手_failure在浏览器中有效。

问题描述 投票:0回答:1

我无法发送简单的GET请求到第三方https网址,而在浏览器中却能正常工作。

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)

我试着按照 相关答案但我没有找到解决方案。

没有证书,我使用的是Java 8,试过的解决方案是添加以下内容 -Djsse.enableSNIExtension=false -Dhttps.protocols=TLSv1.2,TLSv1.1,TLSv1

增加了与浏览器发送相同的头文件 (AcceptUser-Agent),但运气不好

编码

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);
  • 网站使用云端服务

curl也是工作,把完整的url,从verbose输出,它使用服务器证书与。

SSL connection using TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384

也试过用配置 跳过SSL证书验证 具有相同的输出。

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

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

或者也可以用 NoopHostnameVerifier:

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

curl -v results 服务器证书。

*       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

编辑

我把证书添加到java中,作为 @Walk建议:

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

证书被添加到钥匙库

我看到在浏览器中加载了证书,在JMeter中也能正常工作,但使用restTemplate或apache HTTPClient时仍然出现同样的错误。

我使用的是Java 8更新151。

尝试过 解决办法 从@rmunge的回答中添加了BouncyCastleProvider,但还是一样的错误。

security.provider.6=org.bouncycastle.jce.provider.BouncyCastleProvider
java ssl get resttemplate handshake
1个回答
2
投票

根本原因很可能是缺少对最新的基于EC的密码套件的支持,如 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384

椭圆曲线密码学(ECC)是由以下几个方面实现的 SunEC 提供者。为了充分发挥功能,需要以下条件。

  • SunEC必须列出injrelibsecurityjava.security和jdk.tls.disabledAlgorithms的值不能包含ECDHE。
  • 本机库 libsunce.so sunce.dll 必须在 jrelib (Linux, Mac) 或 jrebin (Windows) 文件夹中可用。
  • 确保JDK JCE框架使用无限策略。由于Java 8u161这是新的默认值,对于旧版本,您可能必须安装JCE无限强度管辖策略文件和或在java.security文件中进行更改。请看 此处 以获得进一步的说明。

如果本机库不可用,提供者仍然可以工作,但只提供基于 ECC 的密码子集(参见 SunEC.java). 似乎有些linux发行版明确地删除了本地库或默认禁用了提供者(如 RedHat, 亚马逊Linux). 因此,如果该库不是你的JRE的一部分,请更新到最新的包版本或直接下载并安装最新的OpenJDK 8版本--简单地从下载中复制原生库也是一种选择--参见 此处 比如说,我们可以使用第三方加密技术提供商,比如Bouncy Castle,它有自己的ECC提供商。

另一个选择是使用第三方加密提供商,比如Bouncy Castle,它有自己的ECC提供商。有关指导,请看 这个 问题及其公认的答案。


1
投票

能否请你说明你使用的是哪个版本的Java!!! 如果是Java 7,那么这个就可以帮你解决了......

我可以看到你的客户端解析为TLSv1。openssl 的输出,说明你的服务器不支持 TLSv1。

TLS ver.1和1.2在Java 7中默认被禁用。1.1和1.2在Java 7中默认为禁用。

尽管Java SE 7版本中的SunJSSE支持TLS 1.1和TLS 1.2,但这两个版本在客户端连接时默认都没有启用。有些服务器不能正确实现前向兼容性,拒绝与TLS 1.1或TLS 1.2客户端对话。为了实现互操作性,SunJSSE默认不为客户端连接启用TLS 1.1或TLS 1.2。

启用TLSv1.1和TLSv1.2的方法有两种。

  1. JVM参数。

    -Dhttps.protocols=TLSv1.2,TLSv1.1,TLSv1
    
  2. 或者在Java代码中设置相同的属性

    System.setProperty("https.protocols", "TLSv1.2,TLSv1.1,TLSv1");
    
  3. 或者安装 Java 7的JCE无限强度策略文件. 我不是100%确定这一步是否能解决问题,尽管它总是值得安装JCE,同时它允许JVM使用现有算法的更强版本。

笔记: 在备选方案1和2中,协议的顺序从较好到较差(TLS 1.2版到1版)。

© www.soinside.com 2019 - 2024. All rights reserved.