RestTemplate GET请求中的handshake_failure在浏览器中有效

时间:2020-05-26 07:54:57

标签: java java-8 httpclient resttemplate handshake

我无法将简单的GET请求发送到在浏览器中正常运行的第三方https URL:

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)

我尝试关注related answers,但没有找到解决方法。

没有证书,我正在使用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);
  • 站点使用cloudflare服务

curl也可以通过输入完整的url来工作,从详细的输出来看,它使用带有以下内容的服务器证书:

SSL connection using TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384

还尝试了具有相同输出的配置 skip SSL certificate verification

TrustStrategy acceptingTrustStrategy =(X509Certificate []链,字符串authType)-> true;

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

或者也可以使用NoopHostnameVerifier

CloseableHttpClient httpClient = HttpClients.custom() .setSSLHostnameVerifier(新的NoopH​​ostnameVerifier()) 。建立(); HttpComponentsClientHttpRequestFactory requestFactory =新的HttpComponentsClientHttpRequestFactory(); requestFactory.setHttpClient(httpClient);

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

编辑

我以@Walk suggested的形式向Java添加了证书:

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的solution,添加了BouncyCastleProvider,但仍然存在相同的错误

security.provider.6=org.bouncycastle.jce.provider.BouncyCastleProvider

2 个答案:

答案 0 :(得分:2)

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

椭圆曲线密码术(ECC)由SunEC提供者实现。要充分发挥功能,必须满足以下条件:

  • 必须将SunEC列出 jre / lib / security / java.security和jdk.tls.disabledAlgorithms的值不得包含ECDHE
  • 本机库libsunce.so / sunce.dll必须在jre / lib(Linux,Mac)或jre / bin(Windows)文件夹中可用
  • 确保JDK JCE框架使用无限策略。从Java 8u161开始 这是新的默认值,对于较旧的版本,您可能必须安装JCE无限强度管辖权策略文件和/或在java.security文件中进行更改。有关更多说明,请参见here

如果本机库不可用,则提供程序仍可以工作,但仅提供基于ECC密码的子集(请参见SunEC.java的注释)。似乎某些Linux发行版默认情况下显式删除了本机库或禁用了提供程序(例如RedHatAmazon Linux)。因此,如果该库不应该是您的JRE的一部分,请更新到最新的软件包版本或直接下载并安装最新的OpenJDK 8版本-也可以选择仅从下载中复制本机lib-例如,参见here

另一种选择是使用第三方加密提供程序,例如Bouncy Castle,它具有自己的ECC提供程序。有关说明,请参见this问题及其接受的答案。

答案 1 :(得分:1)

能否请您指定使用的Java版本!!!如果是Java 7,那么它将为您完成工作。.....

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

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

尽管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. 或安装JCE Unlimited Strength policy files for Java 7。我不确定100%是否可以通过此步骤解决问题,尽管在让JVM使用更强大版本的现有算法的同时,始终值得安装JCE。

注意:选项1和2中协议的顺序从好到坏(TLS版本1.2至1)更改。