Java - 使用客户端证书身份验证时取消 SSL 流
Java - Ssl stream is cancelled when using client certificate authentication
这个我想不通。
我有一个单元测试使用客户端证书身份验证执行与我的服务的连接。
// generate a valid client cert and store it in a keystore
String keystorePassword = "xxx";
InputStream pkcs12 = UnitTests.generatePkcs12ForUser(user, keystorePassword, 3600);
KeyStore ks = KeyStore.getInstance("pkcs12");
ks.load(pkcs12, keystorePassword.toCharArray());
String url = getBaseServerUrl();
// prepare a ssl context that has the keystore with client cert and key
SSLContext sslContext = SSLContexts.custom()
.loadKeyMaterial(ks, keystorePassword.toCharArray())
// trust all SSL certs
.loadTrustMaterial((X509Certificate[] chain, String authType) -> true)
.build();
// validate any hostname, and don't follow 3XX responses
HttpClient httpClient = HttpClients.custom()
.setSSLContext(sslContext)
.setSSLHostnameVerifier((a,b) -> true)
.disableRedirectHandling()
.build();
// this fails catastrophically
HttpResponse response = httpClient.execute(new HttpGet(url));
我正在使用 Java 8,我的服务器位于使用 Nginx 的反向代理之后。
我的单元测试失败,出现如下异常:
javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1002)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1385)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1413)
[...]
Caused by: java.io.EOFException: SSL peer shut down incorrectly
at sun.security.ssl.InputRecord.read(InputRecord.java:505)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:983)
... 43 more
而且我可以在 Nginx error.log 中看到以下行:
2018/05/18 15:34:12 [crit] 42#42: *327 SSL_do_handshake() failed (SSL: error:0D0680A8:asn1 encoding routines:asn1_check_tlen:wrong tag error:0D08303A:asn1 encoding routines:asn1_template_noexp_d2i:nested asn1 error) while SSL handshaking, client: 172.18.0.1, server: 0.0.0.0:443
我已经在互联网上搜索了这个错误的原因,我觉得我已经用尽了所有的资源。
我尝试过的事情的非详尽列表:
- 强制 java 将 TlsV1.1 与
-Dhttps.protocols=Tlsv1.1
一起使用 -> 仍然失败
- 试图为
loadTrustMaterial
指定“密钥策略”以始终使用我自己的密钥 -> 仍然失败
- 使用了具有同一组 Ssl /Keystore 参数的 Jersey 客户端 -> 仍然失败
- 尝试了 1.8 的所有 JDK 版本:OpenJDK、Oracle 和 Oracle with Crypto Extensions (JCE)
- 搜索 nginx 错误似乎暗示传递的证书不正确,所以我...
- ...在调试器中将密钥和证书转储到几个 PEM 文件中并
curl
ed 相同的地址 -> 它有效?!
- Wireshark 连接并检查了 TLS 协商。将其与工作示例(上面的简单
curl -k <url>
完美运行)进行比较:
- 看到了一些关于 ALPN 的信息,但在 Java 中启用它并没有修复异常。
- 看到公布了不同的加密算法,但没有什么突出的
你们有什么想法吗?我开始发疯了。我有一种预感,我没有正确设置我的连接,但我看不到哪里。
对于那些没有阅读评论的人:我想通了:我使用 subjectAltName 来存储不是替代名称的东西。
看来Java对此不满意,拒绝连接
这个我想不通。
我有一个单元测试使用客户端证书身份验证执行与我的服务的连接。
// generate a valid client cert and store it in a keystore
String keystorePassword = "xxx";
InputStream pkcs12 = UnitTests.generatePkcs12ForUser(user, keystorePassword, 3600);
KeyStore ks = KeyStore.getInstance("pkcs12");
ks.load(pkcs12, keystorePassword.toCharArray());
String url = getBaseServerUrl();
// prepare a ssl context that has the keystore with client cert and key
SSLContext sslContext = SSLContexts.custom()
.loadKeyMaterial(ks, keystorePassword.toCharArray())
// trust all SSL certs
.loadTrustMaterial((X509Certificate[] chain, String authType) -> true)
.build();
// validate any hostname, and don't follow 3XX responses
HttpClient httpClient = HttpClients.custom()
.setSSLContext(sslContext)
.setSSLHostnameVerifier((a,b) -> true)
.disableRedirectHandling()
.build();
// this fails catastrophically
HttpResponse response = httpClient.execute(new HttpGet(url));
我正在使用 Java 8,我的服务器位于使用 Nginx 的反向代理之后。
我的单元测试失败,出现如下异常:
javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1002)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1385)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1413)
[...]
Caused by: java.io.EOFException: SSL peer shut down incorrectly
at sun.security.ssl.InputRecord.read(InputRecord.java:505)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:983)
... 43 more
而且我可以在 Nginx error.log 中看到以下行:
2018/05/18 15:34:12 [crit] 42#42: *327 SSL_do_handshake() failed (SSL: error:0D0680A8:asn1 encoding routines:asn1_check_tlen:wrong tag error:0D08303A:asn1 encoding routines:asn1_template_noexp_d2i:nested asn1 error) while SSL handshaking, client: 172.18.0.1, server: 0.0.0.0:443
我已经在互联网上搜索了这个错误的原因,我觉得我已经用尽了所有的资源。
我尝试过的事情的非详尽列表:
- 强制 java 将 TlsV1.1 与
-Dhttps.protocols=Tlsv1.1
一起使用 -> 仍然失败 - 试图为
loadTrustMaterial
指定“密钥策略”以始终使用我自己的密钥 -> 仍然失败 - 使用了具有同一组 Ssl /Keystore 参数的 Jersey 客户端 -> 仍然失败
- 尝试了 1.8 的所有 JDK 版本:OpenJDK、Oracle 和 Oracle with Crypto Extensions (JCE)
- 搜索 nginx 错误似乎暗示传递的证书不正确,所以我...
- ...在调试器中将密钥和证书转储到几个 PEM 文件中并
curl
ed 相同的地址 -> 它有效?! - Wireshark 连接并检查了 TLS 协商。将其与工作示例(上面的简单
curl -k <url>
完美运行)进行比较:- 看到了一些关于 ALPN 的信息,但在 Java 中启用它并没有修复异常。
- 看到公布了不同的加密算法,但没有什么突出的
你们有什么想法吗?我开始发疯了。我有一种预感,我没有正确设置我的连接,但我看不到哪里。
对于那些没有阅读评论的人:我想通了:我使用 subjectAltName 来存储不是替代名称的东西。
看来Java对此不满意,拒绝连接