Den*_*ees 118 java ssl ssl-certificate sslhandshakeexception
我有授权SSL连接的问题.我创建了Struts Action,它通过Client Authorized SSL证书连接到外部服务器.在我的行动中我试图将一些数据发送到银行服务器但没有任何运气,因为我从服务器得到以下错误的结果:
error: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
Run Code Online (Sandbox Code Playgroud)
我的Action类从我的Action类发送数据到服务器
//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;
Run Code Online (Sandbox Code Playgroud)
我的merchant.properties文件:
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
Run Code Online (Sandbox Code Playgroud)
我第一次认为这是证书问题,我将它从.pfx转换为.jks,但我有相同的错误,没有任何更改.
Vin*_*lds 231
由于各种原因,可能发生握手失败:
由于无法精确定位基础故障,因此最好打开 -Djavax.net.debug=all标志以启用已建立的SSL连接的调试.打开调试后,您可以查明握手中的哪些活动失败.
更新
根据现有的详细信息,问题似乎是由于颁发给服务器的证书与根CA之间的证书信任路径不完整.在大多数情况下,这是因为信任存储中缺少根CA的证书,导致证书信任路径不存在的情况; 证书基本上不受客户信任.浏览器可以显示警告,以便用户可以忽略这一点,但SSL客户端(例如HttpsURLConnection类或任何HTTP客户端库,如Apache HttpComponents Client)也不会这样.
大多数这些客户端类/库将依赖于JVM用于证书验证的信任存储.在大多数情况下,这将是cacertsJRE_HOME/lib/security目录中的文件.如果已使用JVM系统属性指定了信任库的位置javax.net.ssl.trustStore,则该路径中的存储通常是客户端库使用的存储.如果您有疑问,请查看您的Merchant课程,并找出用于建立连接的类/库.
将服务器的证书颁发CA添加到此信任库应该可以解决问题.您可以参考我关于为此目的获取工具的相关问题的答案,但Java keytool实用程序足以实现此目的.
警告:信任存储本质上是您信任的所有CA的列表.如果您输入的证书不属于您不信任的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:\Java\jdk1.6.0_21\jre\lib\security\cacerts
trustStore type is : jks
trustStore provider is :
Run Code Online (Sandbox Code Playgroud)
如果使用了错误的信任库,那么您需要将服务器的证书重新导入到正确的信任库中,或者重新配置服务器以使用列出的服务器(如果您有多个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
Run Code Online (Sandbox Code Playgroud)
您需要查找服务器的CA是否为主题.
握手过程将有一些显着的条目(您需要知道SSL以详细了解它们,但是为了调试当前问题,知道通常在ServerHello中报告handshake_failure就足够了.
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 }
***
Run Code Online (Sandbox Code Playgroud)
请注意使用的密码套件.这可能必须与您的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
***
Run Code Online (Sandbox Code Playgroud)
注意它选择的密码套件; 这是服务器和客户端都可以使用的最佳套件.通常,如果出现错误,则不指定密码套件.服务器的证书(以及可选的整个链)由服务器发送,并在条目中找到:
*** 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
***
Run Code Online (Sandbox Code Playgroud)
如果证书验证成功,您将找到类似于以下内容的条目:
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
Run Code Online (Sandbox Code Playgroud)
上述步骤之一不会成功,导致handshake_failure,因为握手通常在此阶段完成(不是真的,但握手的后续阶段通常不会导致握手失败).您需要确定哪个步骤失败,并将相应的消息发布为问题的更新(除非您已经理解了该消息,并且您知道如何解决它).
Bri*_*rig 15
当客户端需要提供证书时,也会发生这种情况.服务器列出证书链后,可能会发生以下情况:
3.证书请求 服务器将从客户端发出证书请求.该请求将列出服务器接受的所有证书.
*** 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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
如果链中没有证书且服务器需要证书,那么您将在此处获得握手错误.可能的原因是找不到证书的路径.
5.证书验证 客户端要求服务器验证证书
*** CertificateVerify
... payload of verify check
Run Code Online (Sandbox Code Playgroud)
只有在您发送证书时才会执行此步骤.
6.完成 服务器将响应验证响应
*** Finished
verify_data: { 345, ... }
Run Code Online (Sandbox Code Playgroud)
mot*_*bói 15
我不认为这解决了第一个提问者的问题,但是对于来到这里寻求答案的googlers:
在更新51上,默认情况下java 1.8禁止[1] RC4密码,我们可以在发行说明页面上看到:
错误修复:禁止RC4密码套件
RC4现在被视为受损密码.
已从Oracle JSSE实现中的客户端和服务器默认启用的密码套件列表中删除RC4密码套件.这些密码套件仍然可以通过
SSLEngine.setEnabledCipherSuites()和SSLSocket.setEnabledCipherSuites()方法启用.请参阅JDK-8077109(非公开).
如果您的服务器对此密码具有强烈的偏好(或仅使用此密码),则可以触发handshake_failureon java.
您可以测试连接到启用RC4密码的服务器(首先,尝试不带enabled参数查看是否触发a handshake_failure,然后设置enabled:
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.*;
import java.util.Arrays;
/** Establish a SSL connection to a host and port, writes a byte and
* prints the response. See
* http://confluence.atlassian.com/display/JIRA/Connecting+to+SSL+services
*/
public class SSLRC4Poke {
public static void main(String[] args) {
String[] cyphers;
if (args.length < 2) {
System.out.println("Usage: "+SSLRC4Poke.class.getName()+" <host> <port> enable");
System.exit(1);
}
try {
SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket(args[0], Integer.parseInt(args[1]));
cyphers = sslsocketfactory.getSupportedCipherSuites();
if (args.length ==3){
sslsocket.setEnabledCipherSuites(new String[]{
"SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
"SSL_DH_anon_WITH_RC4_128_MD5",
"SSL_RSA_EXPORT_WITH_RC4_40_MD5",
"SSL_RSA_WITH_RC4_128_MD5",
"SSL_RSA_WITH_RC4_128_SHA",
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
"TLS_ECDHE_RSA_WITH_RC4_128_SHA",
"TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
"TLS_ECDH_RSA_WITH_RC4_128_SHA",
"TLS_ECDH_anon_WITH_RC4_128_SHA",
"TLS_KRB5_EXPORT_WITH_RC4_40_MD5",
"TLS_KRB5_EXPORT_WITH_RC4_40_SHA",
"TLS_KRB5_WITH_RC4_128_MD5",
"TLS_KRB5_WITH_RC4_128_SHA"
});
}
InputStream in = sslsocket.getInputStream();
OutputStream out = sslsocket.getOutputStream();
// Write a test byte to get a reaction :)
out.write(1);
while (in.available() > 0) {
System.out.print(in.read());
}
System.out.println("Successfully connected");
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
Run Code Online (Sandbox Code Playgroud)
1 - https://www.java.com/en/download/faq/release_changes.xml
Bel*_*izy 14
握手失败可能是一个错误的TLSv1协议实现.
在我们的例子中,这有助于java 7:
java -Dhttps.protocols=TLSv1.2,TLSv1.1,TLSv1
Run Code Online (Sandbox Code Playgroud)
jvm将按此顺序进行谈判.具有最新更新的服务器将执行1.2,有缺陷的服务器将下载到v1,并且与java 7中的类似v1一起使用.
小智 8
我尝试使用JDK 1.7时遇到此错误.当我将JDK升级到jdk1.8.0_66时,一切正常.
因此,解决此问题的最简单方法是 - 升级JDK并开始运行良好.
假设您使用正确的 SSL/TLS 协议,正确配置您的keyStore和trustStore,并确认证书本身不存在任何问题,您可能需要加强您的安全算法。
正如Vineet 的回答中所述,您收到此错误的一个可能原因是使用了不兼容的密码套件。通过使用Java Cryptography Extension (JCE) 中提供的内容更新JDK文件夹中的mylocal_policy和US_export_policyjars ,我能够成功完成握手。security
小智 5
在我的情况下,证书被导入,错误仍然存在,通过System.setProperty("https.protocols", "TLSv1.2,TLSv1.1,SSLv3");在连接之前添加来解决这个问题