【问题标题】:TLS with client certificate failing handshake带有客户端证书的 TLS 握手失败
【发布时间】:2019-02-28 18:55:26
【问题描述】:

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

无法找到请求目标的有效认证路径

所以我使用AlwaysTrustSSLConnectionFactory()暂时解决了这个问题。

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

警告:未找到合适的证书 - 在没有客户端的情况下继续 身份验证

在 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 将其添加到密钥库中。有谁知道可能是什么问题,我的握手失败是因为我没有正确包含客户端证书,或者我没有正确地将它添加到密钥库?据我了解,信任库需要包含受信任的根权限的公钥,并且密钥库应该有我的客户端证书。它是否正确?我如何做到这一点?

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

【问题讨论】:

  • 我忘了说我尝试在Postman 中使用客户端证书并且它有效。证书有效。
  • 找不到合适的证书 - 没有客户端身份验证继续意味着客户端在密钥库中找不到服务器发送的 CA 证书列表的匹配证书。然后服务器可能会关闭连接,因为客户端证书尚未发送。确保 .p12 的所有证书和私钥确实导入到 JKS 中,或者直接将 .p12 设置为 javax.net.ssl.keyStore 中的密钥库。如果p12也不行,获取服务器发来的CA列表,对比p12证书的颁发者
  • 感谢 Pedro,问题是我将 .crt 导入到从原始 .p12 客户端证书创建的密钥库中,因此我实际上只将公钥添加到了我的存储中。菜鸟失误。

标签: java ssl https certificate client-certificates


【解决方案1】:

我从 API 开发人员那里收到格式为 .p12 的客户端证书,因此我将其转换为 .crt 并使用 Keytool 将其添加到密钥库中

这就是你出错的地方。将其转换为 .crt 会提取公共证书。您需要做的是convert .p12 文件到 java 密钥库。网络上有很多例子。具体方法见this SO answer

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

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

还要检查您的 JDK 是否配置了 Unlimited Strength Jurisdiction Policy Files,因为您被要求使用的密码需要它。

【讨论】:

  • 谢谢,但似乎问题出在其他地方,我假设我包含这些证书存储的方式是因为我仍然收到Warning: no suitable certificate found - continuing without client authentication。刚刚发现我什至不必将 .p12 转换为 .jks ,因为它们都是密钥库。使用 pkcs12 文件可以得到与使用 .jks 相同的结果。
  • @lovrodoe 我不建议在不转换为 jks 的情况下使用 .p12。我们已经有人犯了这个错误,它影响了 java 代码从私钥条目中提取证书部分的能力。您是否验证了颁发者是正确的并且与您的服务器请求的单个颁发者相匹配?
  • ...如果您还没有拨打电话,请务必放弃setSSLSocketFactory()。如果遇到主机名不匹配的问题,请使用 setHostnameVerifier 和简单的 return true 实现。
  • 干杯安迪,感谢您的回复。你实际上已经让我走上了解决这个问题的道路,所以我会选择你的答案。尽管我使用的是他们发送给我的原始 .p12 密钥库,但我不再设置 SSL 套接字工厂。您能否澄清一下它影响 Java 代码提取证书部分的能力的部分?它的性能是否比使用 Java 的专有 jks 更差?我今天使用它没有发现任何重大问题,至少它有效。
  • 我的措辞很糟糕。那里有很多代码,包括我们使用的产品,它执行KeyStore.getInstance("JKS"),调用load(),然后操作密钥库内容。显然,当格式不是 JKS 时,这会失败。只是将来需要注意的事情,因为那里有相当多的代码假设所有 java 都是 JKS。
【解决方案2】:

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

http-bio-8080-exec-3,阅读:TLSv1.2 警报,长度 = 2
http-bio-8080-exec-3,RECV TLSv1.2 ALERT:致命,handshake_failure
%% 无效:[Session-7,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384]

【讨论】:

  • 根据ssllabsTLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 是服务器最喜欢的密​​码。它在Cipher Suites 部分中列为首位。问题是,我的客户端在ClientHello 中发送它支持的密码列表,这是服务器在 ServerHello 中选择的密码列表。来自日志:*** ServerHello, TLSv1.2 RandomCookie: GMT: -752143001 bytes = Session ID: Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
  • 是的。如果密码不匹配,就会出现no shared cipher 这样的错误。
猜你喜欢
  • 2016-01-18
  • 1970-01-01
  • 2020-02-14
  • 2018-05-15
  • 1970-01-01
  • 1970-01-01
  • 2016-05-22
  • 2019-08-18
  • 1970-01-01
相关资源
最近更新 更多