客户端证书握手失败的 TLS

TLS with client certificate failing handshake

我很困惑我到底需要在哪里包含客户端证书。现在,我的第一个问题是我不信任服务器。我尝试使用默认的 Java 密钥库文件 (cacerts),其中包含 Thawte 和 Digicert,它们是我尝试与之通信的服务器的根权限。我使用 System.setProperty("javax.net.ssl.keyStore", "...") 将该 cacerts 文件设置为密钥库,它不起作用,我将它设置为信任库,它不起作用。我还有

unable to find valid certification path to requested target

所以我使用 AlwaysTrustSSLConnectionFactory().

暂时解决了这个问题

现在的问题是服务器不信任我。我有一个客户端证书,我尝试将它添加到密钥库和信任库中,但无论我做什么,在 ServerHelloDone 之后我得到

Warning: no suitable certificate found - continuing without client authentication

在 Java 的 SSL 调试消息和秘密和密钥消息后的握手失败。这是我的日志的结尾:

http-bio-8080-exec-3, WRITE: TLSv1.2 Handshake, length = 40
http-bio-8080-exec-3, READ: TLSv1.2 Alert, length = 2
http-bio-8080-exec-3, RECV TLSv1.2 ALERT:  fatal, handshake_failure
%% Invalidated:  [Session-7, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384]
http-bio-8080-exec-3, called closeSocket()
http-bio-8080-exec-3, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
    at sun.security.ssl.Alerts.getSSLException(Unknown Source)
    at sun.security.ssl.Alerts.getSSLException(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.recvAlert(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
    at sun.net.www.protocol.https.HttpsClient.afterConnect(Unknown Source)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(Unknown Source)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(Unknown Source)

这是我当前的代码:

System.setProperty("javax.net.ssl.keyStore", "C:/Users/Lovro/Desktop/certs/api/keystore.jks");
System.setProperty("javax.net.ssl.keyStorePassword", "pass");

URL url = new URL(urlRequest);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(new AlwaysTrustSSLContextFactory());
conn.connect();

我从 API 开发人员那里收到格式为 .p12 的客户端证书,所以我将其转换为 .crt 并使用 Keytool[=33 将其添加到密钥库=].有谁知道可能是什么问题,我的握手失败是因为我没有正确包含客户端证书,或者我没有正确地将它添加到密钥库?据我了解,truststore 需要包含 public 受信任根权限的密钥,并且 keystore 应该有我的客户端证书。这个对吗?我该如何实现?

欢迎任何建议,我是 TLS/HTTPS 的新手,不知道我在做什么。

从日志来看,TLS 密码 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 似乎对 TLS 客户端无效。您可能需要检查客户端支持的密码列表。如果密码 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 未包含在密码列表中,您可能需要添加对它的支持。

http-bio-8080-exec-3, READ: TLSv1.2 Alert, length = 2
http-bio-8080-exec-3, RECV TLSv1.2 ALERT: fatal, handshake_failure
%% Invalidated: [Session-7, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384]

I recieved my client certificate from API developers in format .p12, so I converted it to .crt and added that to keystore with Keytool

这就是你出错的地方。将其转换为 .crt 可提取 public 证书。您需要做的是 .p12 文件转换为java 密钥库。网上有很多例子。请参阅 this SO answer 了解具体方法。

要确认它是否有效,运行 keytool -list -keystore <yourkeystore>.jks 并检查你是否有一个 PrivateKeyEntry

当您检查内容时,将 -v 标志添加到 keytool -list 命令并检查 Issuer 字段是否为 CN=test, O=test 因为我们可以从您的日志文件中看到您的服务器需要该机构颁发的客户端证书。

还要检查您的 JDK 是否配置了无限强度权限 Policy Files,因为要求您使用的密码需要它。