当我尝试执行 POST 时,Java 握手失败
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25358823/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Handshake failure with Java when I try to do a POST
提问by user1938742
I try to contact *** and send some data using POST.
我尝试联系 *** 并使用 POST 发送一些数据。
I use jersey-bundle-1.18.jar which I have imported into my Eclipse project. This is my code:
我使用 jersey-bundle-1.18.jar,它已导入到我的 Eclipse 项目中。这是我的代码:
Client client = Client.create();
WebResource webResource = client.resource("https://somesite.com");
MultivaluedMap<String, String> map = new MultivaluedMapImpl();
map.putSingle("key_name", API_KEY_NAME);
map.putSingle("key_value", API_KEY_VALUE);
map.putSingle("message", "Test");
map.putSingle("extension", "+0012345678");
ClientResponse response = webResource.type("application/x-www-form-urlencoded")
.post(ClientResponse.class, map); // throws exception here
return response.toString();
When I run my code I get javax.net.ssl.SSLHandshakeException. The certificate used by the site is from StartCom, and I don't think this CA is in Java's default truststore, at not least in Java 6, according to this poster
当我运行我的代码时,我得到 javax.net.ssl.SSLHandshakeException。该站点使用的证书来自 StartCom,我认为这个 CA 不在 Java 的默认信任库中,至少在 Java 6 中,根据此海报
I ran my program with the JVM flag -Djavax.net.debug=all and this is what I got:
我使用 JVM 标志 -Djavax.net.debug=all 运行我的程序,这就是我得到的:
trigger seeding of SecureRandom
done seeding SecureRandom
Ignoring unavailable cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA256
Ignoring unavailable cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unavailable cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unavailable cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_128_CBC_SHA256
Allow unsafe renegotiation: false
Allow legacy hello messages: true
Is initial handshake: true
Is secure renegotiation: false
main, setSoTimeout(0) called
%% No cached client session
*** ClientHello, TLSv1
RandomCookie: GMT: 1391505439 bytes = { 186, 52, 225, 5, 67, 137, 170, 128, 220, 41, 178, 86, 199, 17, 150, 190, 23, 47, 217, 126, 162, 34, 68, 40, 216, 221, 193, 108 }
Session ID: {}
Cipher Suites: [TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA, SSL_RSA_WITH_RC4_128_SHA, TLS_ECDH_ECDSA_WITH_RC4_128_SHA, TLS_ECDH_RSA_WITH_RC4_128_SHA, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_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_RC4_128_MD5, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
Compression Methods: { 0 }
Extension elliptic_curves, curve names: {secp256r1, sect163k1, sect163r2, secp192r1, secp224r1, sect233k1, sect233r1, sect283k1, sect283r1, secp384r1, sect409k1, sect409r1, secp521r1, sect571k1, sect571r1, secp160k1, secp160r1, secp160r2, sect163r1, secp192k1, sect193r1, sect193r2, secp224k1, sect239k1, secp256k1}
Extension ec_point_formats, formats: [uncompressed]
Extension server_name, server_name: [host_name: ***]
***
[write] MD5 and SHA1 hashes: len = 176
0000: 01 00 00 AC 03 01 53 F1 B0 1F BA 34 E1 05 43 89 ......S....4..C.
0010: AA 80 DC 29 B2 56 C7 11 96 BE 17 2F D9 7E A2 22 ...).V...../..."
0020: 44 28 D8 DD C1 6C 00 00 2A C0 09 C0 13 00 2F C0 D(...l..*...../.
0030: 04 C0 0E 00 33 00 32 C0 07 C0 11 00 05 C0 02 C0 ....3.2.........
0040: 0C C0 08 C0 12 00 0A C0 03 C0 0D 00 16 00 13 00 ................
0050: 04 00 FF 01 00 00 59 00 0A 00 34 00 32 00 17 00 ......Y...4.2...
0060: 01 00 03 00 13 00 15 00 06 00 07 00 09 00 0A 00 ................
0070: 18 00 0B 00 0C 00 19 00 0D 00 0E 00 0F 00 10 00 ................
0080: 11 00 02 00 12 00 04 00 05 00 14 00 08 00 16 00 ................
main, WRITE: TLSv1 Handshake, length = 176
[Raw write]: length = 181
0000: 16 03 01 00 B0 01 00 00 AC 03 01 53 F1 B0 1F BA ...........S....
0010: 34 E1 05 43 89 AA 80 DC 29 B2 56 C7 11 96 BE 17 4..C....).V.....
0020: 2F D9 7E A2 22 44 28 D8 DD C1 6C 00 00 2A C0 09 /..."D(...l..*..
0030: C0 13 00 2F C0 04 C0 0E 00 33 00 32 C0 07 C0 11 .../.....3.2....
0040: 00 05 C0 02 C0 0C C0 08 C0 12 00 0A C0 03 C0 0D ................
0050: 00 16 00 13 00 04 00 FF 01 00 00 59 00 0A 00 34 ...........Y...4
0060: 00 32 00 17 00 01 00 03 00 13 00 15 00 06 00 07 .2..............
0070: 00 09 00 0A 00 18 00 0B 00 0C 00 19 00 0D 00 0E ................
0080: 00 0F 00 10 00 11 00 02 00 12 00 04 00 05 00 14 ................
0090: 00 08 00 16 00 0B 00 02 01 00 00 00 00 17 00 15 ................
[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
main, called closeSocket()
main, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
main, called close()
main, called closeInternal(true)
Exception in thread "main" com.sun.jersey.api.client.ClientHandlerException: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
What's wrong, and how can I make it work?
出了什么问题,我该如何使它工作?
EDIT: As mentioned below, I downloaded Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files, but for Java 7, since this is the version of Java I'm using and copied the jar files to /lib/security as per instructions.
编辑:如下所述,我下载了 Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files,但对于 Java 7,因为这是我使用的 Java 版本,并按照说明将 jar 文件复制到 /lib/security。
I ran the program again, and got stuck at another exception. I've copied a couple of rows from the Eclipse console:
我再次运行该程序,并陷入另一个异常。我从 Eclipse 控制台复制了几行:
%% Invalidated: [Session-1, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA] main, SEND TLSv1 ALERT: fatal, description = certificate_unknown main, WRITE: TLSv1 Alert, length = 2 [Raw write]: length = 7 0000: 15 03 01 00 02 02 2E ....... main, called closeSocket() main, handling exception: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
%% 无效:[Session-1, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA] main,发送 TLSv1 ALERT:fatal,description = certificate_unknown main,WRITE:TLSv1 Alert,length = 2 [Raw write]:length = 7 0000: 15 03 202E0 ...... main,调用closeSocket() main,处理异常:javax.net.ssl.SSLHandshakeException:sun.security.validator.ValidatorException:PKIX路径构建失败:sun.security.provider.certpath.SunCertPathBuilderException:无法找到到请求目标的有效认证路径
This time, it seems like the StartCom certificate in Java's truststore is missing at last. I'm sure it's possible to overcome this problem, but since I consider to deploy my code into a jar file that can be used by other people, I don't think it's a feasible solution to let people replace their JCE Policy files in order to use my library.
这一次,Java 的信任库中的 StartCom 证书似乎终于丢失了。我确信有可能克服这个问题,但由于我考虑将我的代码部署到其他人可以使用的 jar 文件中,我认为让人们按顺序替换他们的 JCE 策略文件不是一个可行的解决方案使用我的图书馆。
I was a bit nonplussed when I first encountered this error because I had actually executed about the same code before with successful results, but when I think about it, I used OpenJDK and not Oracle's proprietary JDK. When I used OpenJDK I didn't had the encryption problem either. So, it seems like I've two bad choices here:
当我第一次遇到这个错误时,我有点不知所措,因为我之前实际上已经执行了大约相同的代码并获得了成功,但是当我想到它时,我使用的是 OpenJDK 而不是 Oracle 的专有 JDK。当我使用 OpenJDK 时,我也没有遇到加密问题。所以,似乎我在这里有两个错误的选择:
1). Force users to switch their Java Development Kit to OpenJDK, or my service/library won't work.
1)。强制用户将他们的 Java 开发工具包切换到 OpenJDK,否则我的服务/库将无法工作。
2). Force users to replace the JCE Policy files of their JRE and import the StartCom certificate into Java's keystore, e.g using keytool, or maybe I could load the certificate during runtime?
2)。强制用户替换其 JRE 的 JCE 策略文件并将 StartCom 证书导入 Java 的密钥库,例如使用 keytool,或者我可以在运行时加载证书?
Is this somewhat correct?
这有点正确吗?
EDIT2:
编辑2:
Here's how I created my own truststore with the certificates available at http://www.startssl.com/certs/ca-bundle.crt
以下是我如何使用http://www.startssl.com/certs/ca-bundle.crt 上提供的证书创建自己的信任库
try {
// Read the certificate bundle from disk
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate ca = cf.generateCertificate(getClass().getResourceAsStream("ca-bundle.crt")); // the file ca-bundle.crt should be in the same folder as your .java files
// Create a KeyStore containing our trusted CAs
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Create a TrustStore that trusts the CAs in our KeyStore
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
// Create an SSLContext that uses our TrustManager
sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
} catch (CertificateException | KeyStoreException | NoSuchAlgorithmException | KeyManagementException | IOException e) {
e.printStackTrace();
}
It's now possible to communicate with the server securely. You can create a HttpClientwith your custom SSLContext like this:
现在可以安全地与服务器通信。您可以使用自定义 SSLContext创建一个HttpClient,如下所示:
CloseableHttpClient client = HttpClients.custom().setSslcontext(sslContext).build();
回答by neomega
Update your Java Cryptography Extension:
更新您的 Java 加密扩展:
Java 6 http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html
Java 6 http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html
Java 7 http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
Java 7 http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
The default JRE comes only with limited cryptographic strength because of the US law.
由于美国法律,默认 JRE 仅具有有限的加密强度。
Edit: It seems the ROOT CA Certificate of StartCom isn't in the java trusted CA. Add it yourself using openssl to cacerts in the JRE (see Digital Certificate: How to import .cer file in to .truststore file).
编辑:StartCom 的 ROOT CA 证书似乎不在 Java 受信任的 CA 中。使用 openssl 将其添加到 JRE 中的 cacerts(请参阅数字证书:如何将 .cer 文件导入到 .truststore 文件)。
You are building a library, just put a notice somewhere in a txt file (README, HOWTO, FAQ, ...) for people with this problem to do the same with the root AC Certificate.
您正在构建一个库,只需在 txt 文件(README、HOWTO、FAQ 等)中的某处放置一条通知,让有此问题的人对根 AC 证书执行相同操作。