我无法发送简单的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
增加了与浏览器发送相同的头文件 (Accept
和 User-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
根本原因很可能是缺少对最新的基于EC的密码套件的支持,如 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
椭圆曲线密码学(ECC)是由以下几个方面实现的 SunEC
提供者。为了充分发挥功能,需要以下条件。
如果本机库不可用,提供者仍然可以工作,但只提供基于 ECC 的密码子集(参见 SunEC.java). 似乎有些linux发行版明确地删除了本地库或默认禁用了提供者(如 RedHat, 亚马逊Linux). 因此,如果该库不是你的JRE的一部分,请更新到最新的包版本或直接下载并安装最新的OpenJDK 8版本--简单地从下载中复制原生库也是一种选择--参见 此处 比如说,我们可以使用第三方加密技术提供商,比如Bouncy Castle,它有自己的ECC提供商。
另一个选择是使用第三方加密提供商,比如Bouncy Castle,它有自己的ECC提供商。有关指导,请看 这个 问题及其公认的答案。
能否请你说明你使用的是哪个版本的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的方法有两种。
JVM参数。
-Dhttps.protocols=TLSv1.2,TLSv1.1,TLSv1
或者在Java代码中设置相同的属性
System.setProperty("https.protocols", "TLSv1.2,TLSv1.1,TLSv1");
或者安装 Java 7的JCE无限强度策略文件. 我不是100%确定这一步是否能解决问题,尽管它总是值得安装JCE,同时它允许JVM使用现有算法的更强版本。
笔记: 在备选方案1和2中,协议的顺序从较好到较差(TLS 1.2版到1版)。