我无法理解这个问题。
我有一个单元测试,使用客户端证书认证连接到我的服务。
// 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的错误日志中看到以下行:
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
我已经在互联网上搜索了很久,想找出这个错误的原因,但感觉已经尝试了所有可能。
以下是我尝试过的一些方法(不全):
- 使用
-Dhttps.protocols=Tlsv1.1
强制Java使用TlsV1.1 -> 仍然失败 - 尝试为
loadTrustMaterial
指定“密钥策略”,始终使用自己的密钥 -> 仍然失败 - 使用相同的SSL /密钥库参数的Jersey客户端 -> 仍然失败
- 尝试了所有JDK 1.8的版本:OpenJDK、Oracle和带有加密扩展(JCE) 的 Oracle
- 查看nginx错误似乎暗示传递的证书不正确,所以我...
- ...在调试模式下将密钥和证书转储到一些PEM文件中,并用
curl
访问相同的地址 -> 它可以正常工作?! - 通过Wireshark检查了TLS握手过程,与一个工作样本进行比较(上面简单的
curl -k <url>
可以顺利工作):- 发现了有关ALPN的内容,但在Java中启用它并不能解决此异常。
- 发现了不同的加密算法,但没有任何明显的问题。
你们有什么想法吗? 我开始感到有些疯狂了。我有一个直觉,认为我没有正确设置连接,但我找不到问题所在。
openssl s_client
吗?这会比仅使用curl
提供更多的调试和参数更改可能性。你也尝试过不使用 NGINX 吗? - Patrick Mevzekcurl --cert file
,那么它不会产生与Java相同的行为,因此不是一个有效的测试。你能发布证书或至少它们的openssl x509 -text
吗(每个)?在问题的Wireshark中,展开客户端证书消息中的每个证书是否正常工作?PS:虽然有些人很古怪,但大多数互联网用户不喜欢被鞭打,所以鞭打并不是寻求帮助的好策略 :) - dave_thompson_085