我无法将简单的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
添加了与浏览器发送的标题相同的标题(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来工作,从详细的输出来看,它使用带有以下内容的服务器证书:
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(新的NoopHostnameVerifier()) 。建立(); 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
答案 0 :(得分:2)
根本原因很可能是缺少对最新的基于EC的密码套件(例如TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
)的支持
椭圆曲线密码术(ECC)由SunEC
提供者实现。要充分发挥功能,必须满足以下条件:
如果本机库不可用,则提供程序仍可以工作,但仅提供基于ECC密码的子集(请参见SunEC.java的注释)。似乎某些Linux发行版默认情况下显式删除了本机库或禁用了提供程序(例如RedHat,Amazon 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:
JVM参数:
-Dhttps.protocols=TLSv1.2,TLSv1.1,TLSv1
或通过Java代码设置相同的属性:
System.setProperty("https.protocols", "TLSv1.2,TLSv1.1,TLSv1");
或安装JCE Unlimited Strength policy files for Java 7。我不确定100%是否可以通过此步骤解决问题,尽管在让JVM使用更强大版本的现有算法的同时,始终值得安装JCE。
注意:选项1和2中协议的顺序从好到坏(TLS版本1.2至1)更改。