收到致命警报:通过 SSLHandshakeException 握手失败
我的授权 SSL 连接有问题。我创建了使用客户端授权的 SSL 证书连接到外部服务器的 Struts Action。在我的操作中,我尝试将一些数据发送到银行服务器,但没有任何运气,因为服务器出现以下错误:
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.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
对于第一个当时我以为这是一个证书问题,我将其从 .pfx 转换为 .jks,但我有同样的错误,没有任何更改。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(21)
握手失败可能由于多种原因而发生:
由于无法查明根本故障,因此最好打开
-Djavax.net.debug=all
标志以启用已建立的 SSL 连接的调试。打开调试后,您可以查明握手中的哪些活动失败了。更新
根据现有的详细信息,问题似乎是由于颁发给服务器的证书与根 CA 之间的证书信任路径不完整造成的。大多数情况下,这是因为信任库中不存在根CA的证书,导致证书信任路径无法存在的情况;该证书本质上不受客户端信任。浏览器可以发出警告,以便用户可以忽略此警告,但 SSL 客户端的情况并非如此(例如 HttpsURLConnection 类,或任何 HTTP 客户端库,如 Apache HttpComponents 客户端)。
大多数这些客户端类/库将依赖 JVM 使用的信任存储来进行证书验证。在大多数情况下,这将是
JRE_HOME/lib/security
目录中的cacerts
文件。如果已使用 JVM 系统属性 javax.net.ssl.trustStore 指定了信任存储的位置,则该路径中的存储通常是客户端库使用的存储。如果您有疑问,请查看您的Merchant
类,并找出它用于建立连接的类/库。将服务器的证书颁发 CA 添加到此信任存储应该可以解决该问题。为此,您可以参考我的关于获取工具的相关问题的回答,但是Java keytool 实用程序 就足够了为此目的。
警告:信任存储本质上是您信任的所有 CA 的列表。如果您放入不属于您不信任的 CA 的证书,则在私钥可用的情况下,可以解密到具有该实体颁发的证书的站点的 SSL/TLS 连接。
更新#2:了解 JSSE 跟踪的输出
JVM 使用的密钥库和信任库通常列在最开始,有点类似于以下内容:
如果使用了错误的信任库,那么您将您需要将服务器的证书重新导入到正确的证书,或者重新配置服务器以使用列出的证书(如果您有多个 JVM,则不建议这样做,并且所有 JVM 都用于不同的需求)。
如果您想验证信任证书列表是否包含所需的证书,则有一个相同的部分,其开头为:
您需要查找服务器的 CA 是否是主题。
握手过程将有一些显着的条目(您需要了解 SSL 才能详细了解它们,但为了调试当前问题,知道 ServerHello 中通常报告握手失败就足够了。
1. ClientHello
初始化连接时,客户端发送的第一条消息是 ClientHello 消息,通常在日志中报告为
:使用的密码套件可能必须与您的
merchant.properties
文件中的条目一致,因为银行的库可能采用相同的约定(如果使用的约定是)。不同,无需担心,因为 ServerHello 会说明这一点,如果密码套件不兼容2.ServerHello
服务器会响应 ServerHello,这将指示连接设置是否可以继续。条目于日志通常具有以下类型:
记下它选择的密码套件;这是服务器和客户端均可使用的最佳套件。如果出现错误,通常不会指定密码套件。服务器的证书(以及可选的整个链)由服务器发送,并且可以在条目中找到,如下所示:
如果证书验证成功,您将找到类似于以下内容的条目:
上述步骤之一不会成功,从而导致握手失败,因为握手通常在此阶段完成(并非真正完成,但握手的后续阶段通常不会导致握手失败)。您需要找出哪个步骤失败了,并发布适当的消息作为问题的更新(除非您已经理解该消息,并且知道如何解决它)。
The handshake failure could have occurred due to various reasons:
Since, the underlying failure cannot be pinpointed, it is better to switch on the
-Djavax.net.debug=all
flag to enable debugging of the SSL connection established. With the debug switched on, you can pinpoint what activity in the handshake has failed.Update
Based on the details now available, it appears that the problem is due to an incomplete certificate trust path between the certificate issued to the server, and a root CA. In most cases, this is because the root CA's certificate is absent in the trust store, leading to the situation where a certificate trust path cannot exist; the certificate is essentially untrusted by the client. Browsers can present a warning so that users may ignore this, but the same is not the case for SSL clients (like the HttpsURLConnection class, or any HTTP Client library like Apache HttpComponents Client).
Most these client classes/libraries would rely on the trust store used by the JVM for certificate validation. In most cases, this will be the
cacerts
file in theJRE_HOME/lib/security
directory. If the location of the trust store has been specified using the JVM system propertyjavax.net.ssl.trustStore
, then the store in that path is usually the one used by the client library. If you are in doubt, take a look at yourMerchant
class, and figure out the class/library it is using to make the connection.Adding the server's certificate issuing CA to this trust store ought to resolve the problem. You can refer to my answer on a related question on getting tools for this purpose, but the Java keytool utility is sufficient for this purpose.
Warning: The trust store is essentially the list of all CAs that you trust. If you put in an certificate that does not belong to a CA that you do not trust, then SSL/TLS connections to sites having certificates issued by that entity can be decrypted if the private key is available.
Update #2: Understanding the output of the JSSE trace
The keystore and the truststores used by the JVM are usually listed at the very beginning, somewhat like the following:
If the wrong truststore is used, then you'll need to re-import the server's certificate to the right one, or reconfigure the server to use the one listed (not recommended if you have multiple JVMs, and all of them are used for different needs).
If you want to verify if the list of trust certs contains the required certs, then there is a section for the same, that starts as:
You'll need to look for if the server's CA is a subject.
The handshake process will have a few salient entries (you'll need to know SSL to understand them in detail, but for the purpose of debugging the current problem, it will suffice to know that a handshake_failure is usually reported in the ServerHello.
1. ClientHello
A series of entries will be reported when the connection is being initialized. The first message sent by the client in a SSL/TLS connection setup is the ClientHello message, usually reported in the logs as:
Note the cipher suites used. This might have to agree with the entry in your
merchant.properties
file, for the same convention might be employed by the bank's library. If the convention used is different, there is no cause of worry, for the ServerHello will state so, if the cipher suite is incompatible.2. ServerHello
The server responds with a ServerHello, that will indicate if the connection setup can proceed. Entries in the logs are usually of the following type:
Note the cipher suite that it has chosen; this is best suite available to both the server and the client. Usually the cipher suite is not specified if there is an error. The certificate of the server (and optionally the entire chain) is sent by the server, and would be found in the entries as:
If the verification of the certificate has succeeded, you'll find an entry similar to:
One of the above steps would not have succeeded, resulting in the handshake_failure, for the handshake is typically complete at this stage (not really, but the subsequent stages of the handshake typically do not cause a handshake failure). You'll need to figure out which step has failed, and post the appropriate message as an update to the question (unless you've already understood the message, and you know what to do to resolve it).
握手失败可能是由于 TLSv1 协议实现存在缺陷所致。
在我们的例子中,这对 java 7 很有帮助:
jvm 将按此顺序进行协商。具有最新更新的服务器将执行 1.2,有问题的服务器将降级到 v1,并且可与 java 7 中的类似 v1 一起使用。
The handshake failure could be a buggy TLSv1 protocol implementation.
In our case this helped with java 7:
The jvm will negotiate in this order. The servers with the latest update will do 1.2, the buggy ones will go down to v1 and that works with the similar v1 in java 7.
安装 Java 加密扩展 (JCE) 无限强度 (对于 JDK7 | 对于 JDK8)可能会修复此错误。解压缩文件并按照自述文件进行安装。
Installing Java Cryptography Extension (JCE) Unlimited Strength (for JDK7 | for JDK8) might fix this bug. Unzip the file and follow the readme to install it.
当客户端需要出示证书时也会发生这种情况。服务器列出证书链后,可能会发生以下情况:
3.证书请求
服务器将向客户端发出证书请求。该请求将列出服务器接受的所有证书。
4.客户端证书链
这是客户端发送到服务器的证书。
如果链中没有证书并且服务器需要一个证书,您将在此处收到握手错误。可能的原因是找不到您的证书的路径。
5.证书验证
客户端要求服务器验证证书
此步骤仅在您发送证书时才会发生。
6.完成
服务器将回复一个验证响应
This can also happend when the client needs to present a certificate. After the server lists the certificate chain, the following can happen:
3. Certificate Request
The server will issue a certificate request from the client. The request will list all of the certificates the server accepts.
4. Client Certificate Chain
This is the certificate the client is sending to the server.
If there isn't a certificate in the chain and the server requires one, you'll get the handshake error here. A likely cause is the path to your certificate wasn't found.
5. Certificate Verify
The client asks the server to verify the certificate
This step will only happen if you are sending a certificate.
6. Finished
The server will respond with a verify response
我不认为这解决了第一个提问者的问题,但对于来这里寻求答案的谷歌人来说:
在更新 51 上,默认情况下 java 1.8 禁止[1] RC4 密码,正如我们在发行说明页面上看到的那样:
如果您的服务器对此密码有强烈的偏好(或仅使用此密码),这可能会在 java 上触发
handshake_failure
。您可以测试连接到启用 RC4 密码的服务器(首先,尝试不使用
enabled
参数来查看是否触发handshake_failure
,然后设置enabled
:1 - https://www.java.com/en/download/faq/release_changes.xml
I don't think this solves the problem to the first questioner, but for googlers coming here for answers:
On update 51, java 1.8 prohibited[1] RC4 ciphers by default, as we can see on the Release Notes page:
If your server has a strong preference for this cipher (or use only this cipher) this can trigger a
handshake_failure
on java.You can test connecting to the server enabling RC4 ciphers (first, try without
enabled
argument to see if triggers ahandshake_failure
, then setenabled
:1 - https://www.java.com/en/download/faq/release_changes.xml
我在尝试使用 JDK 1.7 时遇到此错误。
当我将 JDK 升级到 jdk1.8.0_66 时,一切都开始正常工作。
因此,解决此问题的最简单的解决方案可能是 - 升级您的 JDK,这样它就可以正常工作了。
I have this error while I tried to use JDK 1.7.
When I upgraded my JDK to jdk1.8.0_66 all started to work fine.
So the simplest solution for this problem could be - upgrade your JDK and it could start to work well.
就我而言,导入了证书,仍然存在错误,通过在连接之前添加
System.setProperty("https.protocols", "TLSv1.2,TLSv1.1,SSLv3"); 解决了这个问题
In my case, cert is imported, error remains, solved this by adding
System.setProperty("https.protocols", "TLSv1.2,TLSv1.1,SSLv3");
before connect假设您使用正确的 SSL/TLS 协议,正确配置
keyStore
和trustStore
,并确认证书本身不存在任何问题,您可以需要加强您的安全算法。正如 Vineet 的回答中提到的,您收到此错误的一个可能原因是使用了不兼容的密码套件。通过将我的 JDK 的
security
文件夹中的local_policy
和US_export_policy
jar 更新为 Java 加密扩展 (JCE),我能够成功完成握手。Assuming you're using the proper SSL/TLS protocols, properly configured your
keyStore
andtrustStore
, and confirmed that there doesn't exist any issues with the certificates themselves, you may need to strengthen your security algorithms.As mentioned in Vineet's answer, one possible reason you receive this error is due to incompatible cipher suites being used. By updating my
local_policy
andUS_export_policy
jars in my JDK'ssecurity
folder with the ones provided in the Java Cryptography Extension (JCE), I was able to complete the handshake successfully.配置为 The connection failed with
handshake_failure
,HTTPS 服务器就会以这种方式失败我发现如果我的 Java 客户端进程在
ServerHello
成功完成后但在数据流启动之前。没有明确的错误消息来识别问题,该错误看起来就像
我通过尝试使用和不使用“
-Djsse.enableSNIExtension=false
”选项来隔离问题I found an HTTPS server which failed in this way if my Java client process was configured with
The connection failed with
handshake_failure
after theServerHello
had finished successfully but before the data stream started.There was no clear error message that identified the problem, the error just looked like
I isolated the issue by trying with and without the "
-Djsse.enableSNIExtension=false
" option我今天使用 OkHttp 客户端获取基于 https 的 url 时遇到了同样的问题。 服务器端与客户端的Https协议版本和Cipher方法不匹配导致。
1) check your website https Protocol version and Cipher method.
openssl>s_client -connect your_website.com:443 -showcerts
你会得到很多详细信息,关键信息如下:
2) config your http client, for example, in OkHttp client case:
这样就得到了我们想要的。
I meet the same problem today with OkHttp client to GET a https based url. It was caused by Https protocol version and Cipher method mismatch between server side and client side.
1) check your website https Protocol version and Cipher method.
openssl>s_client -connect your_website.com:443 -showcerts
You will get many detail info, the key info is listed as follows:
2) config your http client, for example, in OkHttp client case:
This will get what we want.
我正在使用 com.google.api http 客户端。当我与公司内部站点通信时,由于错误地使用了 https 而不是 http,因此出现了此问题。
I am using com.google.api http client. When I communicate with an internal company site, I got this problem when I mistakenly used https, instead of http.
哎呀!对我来说这只是 Java 版本问题。我使用 JRE 1.6 出现握手错误,使用 JRE 1.8.0_144 一切正常。
Ugg! This turned out to simply be a Java version issue for me. I got the handshake error using JRE 1.6 and everything worked perfectly using JRE 1.8.0_144.
我的错误是
TLS
版本不兼容。以前是
TLSv1
我将其更改为TLSV1.2
这解决了我的问题。Mine was a
TLS
version incompatible error.Previously it was
TLSv1
I changed itTLSV1.2
this solved my problem.我也有类似的问题;升级到 Apache HTTPClient 4.5.3 修复了该问题。
I had a similar issue; upgrading to Apache HTTPClient 4.5.3 fixed it.
就我而言,该网站只能使用 TLSv1.2。我使用apache httpclient 4.5.6,我使用此代码并安装jce来解决此问题(JDK1.7):
jce
jdk7 http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
jdk 8 http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
代码:
In my case, the website just can use TLSv1.2. and i use apache httpclient 4.5.6, i use this code and install jce to solve this (JDK1.7):
jce
jdk7 http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
jdk 8 http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
code:
要从开发人员(第 1 项)和系统管理员(第 2 项和第 3 项)的角度进行故障排除:
-Djavax.net.debug=ssl:handshake:verbose
在 Java 中启用 SSL 握手调试。sudo apt install ssldump
在服务器上安装 ssldump 或按照此 链接如果您在运行以下步骤时观察到密码中的未知值
。ssldump 日志握手不工作的示例:
ssldump 日志成功握手的示例
Java 日志不工作的示例
To troubleshoot from developer (item 1) and system admin (item 2 and 3) perspective:
-Djavax.net.debug=ssl:handshake:verbose
.sudo apt install ssldump
or compile from source by following this link if you observeUnknown value
in cipher when you run below step.sudo ssldump -k <your-private-key> -i <your-network-interface>
Example of not working handshake of ssldump log:
Example of successful handshake of ssldump log
Example of not working Java log
经过检查,我发现问题是 java 7 ,在我将其更改为 java 8 后,它开始工作。下面是有问题的版本。
On checking i found the issue was java 7, after I changed it to java 8 , it started working. Below one is the problematic version.
免责声明:我不知道答案是否对很多人有帮助,只是分享,因为它可能。
我在使用 Parasoft SOATest 发送请求 XML(SOAP) 时收到此错误。
问题是,在添加证书并进行身份验证后,我从下拉列表中选择了错误的别名。
Disclaimer : I am not aware if the answer will be helpful for many people,just sharing because it might .
I was getting this error while using Parasoft SOATest to send request XML(SOAP) .
The issue was that I had selected the wrong alias from the dropdown after adding the certificate and authenticating it.
就我而言,1.1 版本有一个问题。我用curl 轻松重现了这个问题。服务器不支持低于 TLS1.2 的版本。
这收到了握手问题:
在 1.2 版本中,它工作正常:
服务器正在运行 Weblogic,并且在 setEnvDomain.sh 中添加此参数使其可以与 TLSv1.1 一起使用:
In my case I had one issue with the version 1.1. I was reproducing the issue easily with curl. The server didn't support lower versions than TLS1.2.
This received handshake issue:
With version 1.2 it was working fine:
The server was running a Weblogic, and adding this argument in setEnvDomain.sh made it to work with TLSv1.1:
出现这个问题是因为java版本的问题。我使用 1.8.0.231 JDK 并收到此错误。我已经将我的java版本从1.8.0.231降级到1.8.0.171,现在工作正常。
This issue is occurring because of the java version. I was using 1.8.0.231 JDK and getting this error. I have degraded my java version from 1.8.0.231 to 1.8.0.171, Now It is working fine.
就我而言,问题是由于我的 Linux 发行版 (Alt Linux 10) 中的 java-11 损坏而发生的。
首先,检查您的应用程序使用哪些 TLS。例如,使用
-Djavax.net.debug=ssl:handshake:verbose
/usr/bin/java -Djavax.net.debug=ssl:handshake:verbose -jar /jenkins_slave 运行应用程序/slave.jar -jnlpUrl https://jenkins-master.jnlp 秘密 -workDir /jenkins_slav
e我看到:
TLSv1.2。
检查哪些密码支持您的网站:
将脚本中的 -tls1_2 替换为您的 TLS 版本。
然后运行脚本:
server_address 应该没有完整路径并且没有 https。
示例:
然后我收到结果:
然后安装 java-devel 包(我的发行版中的 java-11-openjdk-devel)并运行(一行命令):
我收到:
没有匹配项 所以,我的 java 不支持任何芯片使用我的网站。
解决办法:通过其他方式安装java。只需替换损坏的 java(我从 Alt Linux 8 在 Alt Linux 9 上安装了 java),
如果可以的话,还可以创建错误报告。我已经向 Alt linux p10 团队创建了 bug:https://bugzilla.altlinux。 org/show_bug.cgi?id=47484#c1
In my case issue occurred due to broken java-11 in my Linux distr (Alt Linux 10).
First, check which TLS use your app. For example, run app with
-Djavax.net.debug=ssl:handshake:verbose
/usr/bin/java -Djavax.net.debug=ssl:handshake:verbose -jar /jenkins_slave/slave.jar -jnlpUrl https://jenkins-master.jnlp SECRET -workDir /jenkins_slav
eI saw:
TLSv1.2.
Check which ciphers support your site:
Replace -tls1_2 in script to your TLS version.
Then run script:
server_address should be without full path and without https.
Example:
Then i receive results:
Then install java-devel package (java-11-openjdk-devel in my distr) and run (one-line command):
I receive:
There are no matches So, my java does not support any chiphers which use my-site.
Solution: install java via other ways. Just replace broken java (I installed java on Alt Linux 9 from Alt Linux 8)
Also create bug-report if you can. I have created bug to Alt linux p10 team: https://bugzilla.altlinux.org/show_bug.cgi?id=47484#c1