【问题标题】:Error javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure错误 javax.net.ssl.SSLHandshakeException:收到致命警报:handshake_failure
【发布时间】:2016-10-07 08:28:19
【问题描述】:

我正在尝试使用 Spring RestTemplate 调用 POST Rest 调用:

HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();

RestTemplate restTemplate = new RestTemplate(requestFactory);

HttpHeaders headers = new HttpHeaders();

headers.setContentType(MediaType.APPLICATION_XML);

HttpEntity<GetBalanceHistoryRequest> request1 = new     HttpEntity<GetBalanceHistoryRequest>(request, headers);
String result = restTemplate.postForObject("https://server.com/getBalance", request1, String.class);

https://server.com 有一个证书:webapi.tartu-x86.p12 我将证书导入到 C:\Java_8\jre\lib\security\cacerts usinf keytool

运行我的代码后,我收到以下错误:

SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]
trustStore is: C:\Java_8\jre\lib\security\cacerts
trustStore type is : jks
trustStore provider is : 
init truststore
adding as trusted cert:
  Subject: CN=Equifax Secure Global eBusiness CA-1, O=Equifax Secure Inc., C=US
  Issuer:  CN=Equifax Secure Global eBusiness CA-1, O=Equifax Secure Inc., C=US
  Algorithm: RSA; Serial number: 0xc3517
  Valid from Mon Jun 21 07:00:00 IDT 1999 until Mon Jun 22 07:00:00 IDT 2020

adding as trusted cert:
  Subject: CN=SecureTrust CA, O=SecureTrust Corporation, C=US
  Issuer:  CN=SecureTrust CA, O=SecureTrust Corporation, C=US
....
....

** Finished
verify_data:  { 31, 64, 180, 145, 192, 1, 180, 119, 86, 70, 247, 140 }
***
[write] MD5 and SHA1 hashes:  len = 16
0000: 14 00 00 0C 1F 40 B4 91   C0 01 B4 77 56 46 F7 8C  .....@.....wVF..
Padded plaintext before ENCRYPTION:  len = 48
0000: 14 00 00 0C 1F 40 B4 91   C0 01 B4 77 56 46 F7 8C  .....@.....wVF..
0010: 3F 56 B1 14 65 F3 18 C6   B3 98 D3 50 65 AC 74 1A  ?V..e......Pe.t.
0020: 48 11 50 C0 0B 0B 0B 0B   0B 0B 0B 0B 0B 0B 0B 0B  H.P.............
main, WRITE: TLSv1 Handshake, length = 48
[Raw write]: length = 53
0000: 16 03 01 00 30 B6 A0 43   3D 91 3A C1 F6 34 F5 73  ....0..C=.:..4.s
0010: 54 A7 1A 46 84 42 1A DC   0D 4D B9 4A C1 3F CB A6  T..F.B...M.J.?..
0020: 57 C6 5D DF C4 1D 62 22   92 FB 1F 3E F1 05 0C 5C  W.]...b"...>...\
0030: 56 9E 9B 02 2D                                     V...-
[Raw read]: length = 5
0000: 15 03 01 00 02                                     .....
[Raw read]: length = 2
0000: 02 28                                              .(
main, READ: TLSv1 Alert, length = 2
main, RECV TLSv1 ALERT:  fatal, handshake_failure
%% Invalidated:  [Session-1, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA]
main, called closeSocket()
main, handling exception: javax.net.ssl.SSLHandshakeException: Received                 fatal alert: handshake_failure
 main, called close()
 main, called closeInternal(true)

我使用的是 Java 1.8.0_91

有人可以帮忙吗?

【问题讨论】:

    标签: java certificate ssl-certificate resttemplate client-certificates


    【解决方案1】:

    handshake_failure 等 java SSL 问题的原因通常是这些:

    • 不兼容的密码套件:客户端必须使用服务器启用的密码套件
    • SSL/TLS 的不兼容版本:客户端必须确保它使用兼容的版本。例如,服务器可能会强制在 java7 中默认未启用的 TLS1.2(不是您的情况)
    • 服务器证书的不完整信任路径:客户端可能不信任服务器证书。通常解决方法是将服务器证书链导入客户端信任库。
    • 错误的服务器配置,例如颁发给不同域的证书或证书链不完整。如果修复在服务器部分

    在您的情况下,java8 默认使用 TLSv1.2 时似乎已选择 TLSv1,因此服务器可能未更新为最新版本。

    我建议调试协议消息 ClientHello 和 ServerHello 以观察套件中选择的协议和密码。

    -Djavax.net.debug=ssl
    

    你也可以在SSLLabs查看你的服务器状态

    【讨论】:

    • 感谢您的重播,我如何告诉 java 使用 TLSv1?
    • java8 默认会使用 TLSv1.2,但如果不可用,则会使用 TLSv1.1 或 TLSv1。如果连接选择 TLSv1,那是因为服务器不支持 TLSv1.2 或没有通用密码
    【解决方案2】:

    我遇到了同样的问题,我是这样解决的:

    1 - 将您的 p12 文件转换为 Java KeyStore 文件(.jks 扩展名)并放入项目的资源文件夹中:

    keytool -importkeystore -srckeystore <pfxfilename.pfx> -srcstoretype pkcs12 -destkeystore <jks-filename.jks> -deststoretype JKS
    

    2 - 实现一个请求工厂来使用你的 JKS 文件:

    RestTemplate restTemplate = new RestTemplate();
    
    ClientHttpRequestFactory requestFactory = null;
    try {
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        keyStore.load(
                this.getClass().getClassLoader().getResourceAsStream("jks-filename.jks"), //put your .jks file name here.
                "password".toCharArray()); //the .jks file password.
    
        SSLConnectionSocketFactory socketFactory = 
                new SSLConnectionSocketFactory(
                        new SSLContextBuilder()
                        .loadTrustMaterial(null, new TrustSelfSignedStrategy())
                        .loadKeyMaterial(keyStore, "password".toCharArray()).build()); //the .jks file password again...
    
        HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
        requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
    
    } catch (Exception e) {
        e.printStackTrace();
    }
    
    restTemplate.setRequestFactory(requestFactory);
    

    3 - 您可能需要将 Apache Httpclient 添加到您的项目中。在 Gradle 项目中,添加:

    compile ("org.apache.httpcomponents:httpclient")
    

    准备好了!

    另一种快速的解决方案是生成 JKS 文件并将文件和密码作为 JVM 参数:

    -Djavax.net.ssl.keyStore=/absolute/file/location/java/application/keystore.jks
    -Djavax.net.ssl.keyStorePassword=jkspassword
    

    或放入java类静态块:

    System.setProperty("javax.net.ssl.keyStore", "/absolute/file/location/java/application/keystore.jks");
    System.setProperty("javax.net.ssl.keyStorePassword", "jkspassword");
    

    【讨论】:

      猜你喜欢
      • 2015-12-04
      • 2012-06-18
      • 2016-07-22
      • 2017-01-20
      • 2016-11-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多