java - 收到致命警告:handshake_failure通过 SSLHandshakeException

我的授权SSL连接有问题,


error: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

将数据发送到服务器的方法


//Getting external IP from host
 URL whatismyip = new URL("http://automation.whatismyip.com/n09230945.asp");
 BufferedReader inIP = new BufferedReader(new InputStreamReader(whatismyip.openStream()));

 String IPStr = inIP.readLine(); //IP as a String

 Merchant merchant;

 System.out.println("amount:" + amount +", currency:" + currency +", clientIp:" + IPStr +", description:" + description);

 try {

 merchant = new Merchant(context.getRealPath("/") +"merchant.properties");

 } catch (ConfigurationException e) {

 Logger.getLogger(HomeAction.class.getName()).log(Level.INFO,"message", e);
 System.err.println("error:" + e.getMessage());
 return ERROR;
 }

 String result = merchant.sendTransData(amount, currency, IPStr, description);

 System.out.println("result:" + result);

 return SUCCESS;

我的merchant.proprietes文件:


bank.server.url=https://-servernameandport-/
https.cipher=-cipher-

keystore.file=-key-.jks
keystore.type=JKS
keystore.password=-password-
ecomm.server.version=2.0

encoding.source=UTF-8
encoding.native=UTF-8

我认为这是一个证书问题,将它从.pfx转换为.jks,但还是出现相同的错误。

时间:

握手失败可能发生的原因有多种:

  • 客户端和服务器正在使用的不兼容的密码套件。 这将要求客户端使用服务器支持的( 或者启用) 密码套件。
  • 使用的SSL版本不兼容( 服务器只能接受 TLS v1,而客户端只能使用 SSL v3 ) 。 客户端可能需要确保它使用了兼容的SSL/TLS 协议版本。
  • 服务器证书的不完全信任路径;服务器可能不信任服务器的证书。 这通常会导致更详细的错误,但这很可能。 通常修复是将服务器证书的CA导入客户端存储的信任。
  • cerificate是为不同的域颁发的。 再次,这将导致更详细的消息,但我将在这里状态,以防这是这一原因。 这种情况下的解决方法是让服务器( 它似乎不是你的) 使用正确的证书。

以后,无法发现潜在的故障,最好是在 switch -Djavax.net.debug=all 国旗启用调试ssl连接的建立。 启用调试后,你可以查明握手中的哪些活动失败了。

更新

基于细节来看,似乎这个问题是由于一个不完整的证书信任路径之间的证书发布到服务器,和一个 root ca。 在大多数情况下,这是因为ca的root 证书在信任存储区中不存在,导致证书信任路径无法存在;证书本质上由客户端不信任。 浏览器可以提出一个警告,这样用户可以忽略,但同样不是ssl客户( 像 HttpsURLConnection 类,或者任何HTTP客户端库,如 Apache HttpComponents客户端) 。

大多数客户端类/库都依赖于JVM用于证书验证的信任存储。 在大多数情况下,这将是在 JRE_HOME/lib/security cacerts 文件目录。 如果使用JVM系统属性 javax.net.ssl.trustStore 指定信任存储的位置,那么该路径中的存储通常是客户端库使用的存储库。 如果你有疑问,看看你 Merchant 类,并找出类/库使用的连接。

将颁发CA的服务器证书添加到这个信任存储区应该解决这个问题。 你可以参考我的回答相关的问题让工具为此,但 Javakeytool实用为此就足够了。

警告: 信任存储实质上是你信任的所有CAs的列表。 如果你放入不属于你不信任的CA的证书,那么如果私钥可用,则可以对具有该实体颁发证书的站点的SSL/TLS 连接进行解密。

更新 #2: 理解jsse的输出跟踪

JVM使用的密钥库和信任库通常在最开始,有些类似于以下内容:


keyStore is : 
keyStore type is : jks
keyStore provider is : 
init keystore
init keymanager of type SunX509
trustStore is: C:Javajdk1.6.0_21jrelibsecuritycacerts
trustStore type is : jks
trustStore provider is : 

如果使用错误的信任存储库,那么你将需要re-import证书服务器,或重新配置服务器使用一个( 如果你有多个 jvm,并且所有的jvm都用于不同的需求,建议不要使用) 上市。

如果要验证信任证书列表是否包含所需的,则有一个相同的部分,即:


adding as trusted cert:
 Subject: CN=blah, O=blah, C=blah
 Issuer: CN=biggerblah, O=biggerblah, C=biggerblah
 Algorithm: RSA; Serial number: yadda
 Valid from SomeDate until SomeDate

你需要查找服务器的CA是否为主题。

握手过程将有几个突出的条目(你将需要知道SSL了解它们在细节,但用于调试程序的当前问题,它就足以知道,handshake_failure通常报道了在ServerHello中。

1.ClientHello

初始化连接时将报告一系列条目。 客户端在 SSL/TLS 连接安装程序中发送的第一条消息是ClientHello消息,通常在日志中报告为:


*** ClientHello, TLSv1
RandomCookie: GMT: 1291302508 bytes = { some byte array }
Session ID: {}
Cipher Suites: [SSL_RSA_WITH_RC4_128_MD5, SSL_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_DES_CBC_SHA, SSL_DHE_RSA_WITH_DES_CBC_SHA, SSL_DHE_DSS_WITH_DES_CBC_SHA, SSL_RSA_EXPORT_WITH_RC4_40_MD5, SSL_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA]
Compression Methods: { 0 }
***

注意使用的密码套件。 这可能不得不同意的项在 merchant.properties 文件中,为相同的约定可能受雇于图书馆的银行。 如果使用该公约是不同的,没有担心的原因,ServerHello将状态,如果密码套件是不相容的。

2.ServerHello

服务器用ServerHello响应,这将表明连接安装是否可以继续。 日志中的条目通常是以下类型:


*** ServerHello, TLSv1
RandomCookie: GMT: 1291302499 bytes = { some byte array}
Cipher Suite: SSL_RSA_WITH_RC4_128_SHA
Compression Method: 0
***

注意它选择的密码套件;这对服务器和客户端都是可用的。 如果有错误,通常没有指定密码套件。 服务器( 并且可以选择整个链) 证书由服务器发送,将在以下条目中找到:


*** Certificate chain
chain [0] = [
[
 Version: V3
 Subject: CN=server, O=server's org, L=server's location, ST =Server's state, C=Server's country
 Signature Algorithm: SHA1withRSA, OID = some identifer

.... the rest of the certificate
***

如果证书验证成功,你将发现类似于以下内容的条目:


Found trusted certificate:
[
[
 Version: V1
 Subject: OU=Server's CA, O="Server's CA's company name", C=CA's country
 Signature Algorithm: SHA1withRSA, OID = some identifier

one上述步骤将包括强奸和澳大利亚外交部长、支离 handshake_failure in typically is、handshake for ( 实际上,但握手的后续阶段通常不会导致握手失败) 培训班。第at this 你需要找出哪些步骤失败了,和相应的消息后作为( 除非你已经理解了该消息,并且你知道如何解决它) 更新问题。

在服务器列出证书链之后,发生以下情况:

请求将列出服务器接受的所有证书。


*** CertificateRequest
Cert Types: RSA
Cert Authorities:
<CN=blah, OU=blah, O=blah, L=blah, ST=blah, C=blah>
<CN=yadda, DC=yadda, DC=yadda>
<CN=moreblah, OU=moreblah, O=moreblah, C=moreblah>
<CN=moreyada, OU=moreyada, O=moreyada, C=moreyada>
... the rest of the request
*** ServerHelloDone

4.客户端证书链,这是客户端向服务器发送的证书。


*** Certificate chain
chain [0] = [
[
 Version: V3
 Subject: EMAILADDRESS=client's email, CN=client, OU=client's ou, O=client's Org, L=client's location, ST=client's state, C=client's Country
 Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5
 ... the rest of the certificate
*** ClientKeyExchange, RSA PreMasterSecret, TLSv1 
... key exchange info 

如果链中没有证书,服务器需要一个证书,那么这里将获得握手错误,可能的原因是找不到证书的路径。

5.证书验证客户端,是否要求服务器验证证书


*** CertificateVerify
... payload of verify check

只有在发送证书的情况下才会执行此步骤。

6.已完成服务器将使用验证响应响应


*** Finished
verify_data: { 345, ... }

...