【问题标题】:handshake_failure in RestTemplate GET Request which works in browserRestTemplate GET Request 中的 handshake_failure 在浏览器中工作
【发布时间】:2020-09-12 23:06:10
【问题描述】:

我无法向在浏览器中正常工作的第 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)

我尝试关注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[] chain, String authType) -> true;

SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom() .loadTrustMaterial(null, 接受TrustStrategy) .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

编辑

我将证书添加到 java 为 @Walk suggested:

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

【问题讨论】:

  • 你可以使用 cURL 或类似的方法来请求吗?也许你的浏览器有一个与 Java 或类似的不同的信任库。
  • @Smutje curl 通过放置完整的网址来工作
  • 是否启用了证书检查?
  • @Nish 它也发生在默认的restTemplate为new RestTemplate()
  • 我相信@Smutje 可能是对的。也许您的 java 信任库没有服务器 CA 证书,您可以尝试将其添加到您的 java 信任库中,这将是我的第一次尝试。

标签: java java-8 httpclient resttemplate handshake


【解决方案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% 确定这一步是否能解决问题,尽管安装 JCE 总是值得的,同时它允许 JVM 使用现有算法的更强大版本。

注意:在选项 1 和 2 中,协议的顺序由好转坏(TLS 版本 1.2 到 1)。

【讨论】:

    【解决方案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 Unlimited Strength Jurisdiction Policy Files 和/或在 java.security 文件中进行更改。请参阅here 了解更多说明。

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

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

    【讨论】:

    • 确实我在 ssl 日志中有 Ignoring unavailable cipher suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
    • 我无法执行 jrunscript -e 'exit (javax.crypto.Cipher.getMaxAllowedKeyLength("RC5") &gt;= 256);' || if [ $? -eq 1 ]; then echo "JCE Installed"; else echo "JCE Not Installed or Error"; fi 错误 $? was unexpected at this time.libsunec.so 在 openjdk github 项目中找不到文件
    • javax.crypto.Cipher.getMaxAllowedKeyLength("RC5") &gt;= 256 return false
    • openjdk github 项目仅包含相应的源:hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/tip/src/share/native/… OpenJDK 从adoptopenjdk.net 构建,例如包含 libsunec.so 文件。我从采用openjdk.net 下载的 64 位 linux 构建包含 jre/lib/amd64 中的文件
    • 你有什么确切的java版本?
    猜你喜欢
    • 2014-01-16
    • 1970-01-01
    • 2015-06-18
    • 1970-01-01
    • 2021-03-01
    • 2020-03-24
    • 2020-05-18
    • 1970-01-01
    • 2018-06-01
    相关资源
    最近更新 更多