微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

bad_certificate 使用带有改造的客户端证书okhhtp时出错,但与 curl

如何解决bad_certificate 使用带有改造的客户端证书okhhtp时出错,但与 curl

我在使用改造、okhttp-tls 和 Spring Boot 时遇到了 mTLS 的一些问题。 如果 server.ssl.client-auth 未设置为 need,则通信工作正常,因此服务器的身份验证工作正常。

问题是,当 server.ssl.client-auth 设置为 need(应该如此)时,尝试使用改造结果进行 API 调用

javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate

在服务器端:

*** Certificate chain
<Empty>
***
https-jsse-nio-8443-exec-4,Fatal error: 43: null cert chain
javax.net.ssl.SSLHandshakeException: null cert chain

客户端证书没问题 - 我可以使用 curl 进行相同的调用

cat file_with_private_key.pem file_with_certificate.pem > combined.pem 
curl https:example.com/path --cert  combined.pem

服务器然后显示证书链并返回响应。

证书配置代码大概是这样的(实际代码我就不贴了):


KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(keystoreInputStream,keystorePass);

X509Certificate clientCert = (X509Certificate) keystore.getCertificate(clientCertAlias);
PrivateKey clientKey  = (PrivateKey) keystore.getKey(clientCertAlias,keystorePass);
KeyPair clientKeyPair = new KeyPair(clientCert.getPublicKey(),clientKey);

HeldCertificate heldCertificate = new HeldCertificate(clientKeyPair,clientCert);

HandshakeCertificates handshakeCerts = new HandshakeCertificates.Builder()
        .addplatformTrustedCertificates()
        .heldCertificate(heldCertificate)
        .build();



OkHttpClient client = new OkHttpClient.Builder()
    .sslSocketFactory(handshakeCerts.sslSocketFactory(),handshakeCerts.trustManager())
    .build();

OkHttpClient 客户端变量稍后传递给改造。 当服务器和客户端证书都是自签名时,完全相同的代码可以正常工作,但现在它不适用于 CA 签名证书。

如果我调用 heldCertificate.certificatePem(),我会得到与 file_with_certificate.pem 文件中的 BEGIN CERTIFICATE 块完全相同的字符串。 如果我调用 heldCertificate.privateKeyPkcs1Pem(),我会得到与 file_with_private_key.pem 文件中的 BEGIN RSA PRIVATE KEY 块完全相同的字符串。

我使用的是改造 2.4.0、okhttp-tls 3.14.6、java 8,我将无法升级

编辑: 'file_with_certificate.pem' 只包含一个证书(中间 CA 签署的叶证书)。

编辑2: 我用作密钥库的 pkcs12 文件包含整个证书链(叶证书、中间证书、根证书)和叶证书私钥。尽管 keytool 列出了 3 个带有 keytool -list -rfc 的证书,但似乎只有叶证书实际上是从密钥库中加载的。

我通过从单独的文件导入每个证书并给每个证书一个单独的别名来解决这个问题,然后迭代别名以加载每个证书,如下所示:

HandhsakeCertificates.Builder handshakeCertBuilder = new HandhsakeCertificates.Builder()
.addplatformTrustedCertificates()
.heldCertificate(heldCertificate);

Collections.list(keyStore.aliases())
.foreach(alias -> {
X509Certificate cert = (X509Certificate) keystore.getCertificate(alias);
handshakeCertBuilder.addTrustedCertificate(cert);
})

编辑3: 需要将中间证书添加到服务器信任库,以便服务器信任客户端叶证书。 感谢@SteffenUlrich 提出的问题为我指明了正确的方向。 我不确定为什么 curl 使用叶证书工作 - 当时服务器的“可接受的 CA 名称”部分中唯一可用的 CA 是根证书,所以叶证书不应该被接受(因为它由中间 CA 签名,而不是根 CA)。

解决方法

查看您的设置,我可以得出结论,您的客户端密钥对和您的服务器可信证书存在于同一个密钥库文件中。我建议将它们分成包含客户端身份又名密钥对的 identity.jks 。以及包含所有受信任证书的 truststore.jks。这将使它更易于维护。

我将根据您当前的设置提供此示例,因此请记住,所有内容都存储在单个密钥库文件中。而且我还注意到您使用了 HandshakeCertificatesHeldCertificate,我认为它们不需要,因为默认的 jdk 库已经提供了使用 Retrofit 配置 OkHttp 所需的对象

你能试试下面的代码片段并分享你的结果吗:

KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(keystoreInputStream,keystorePass);

KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore,"your-key-password".toCharArray());
KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();

TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers,trustManagers,null);
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

OkHttpClient client = new OkHttpClient.Builder()
    .sslSocketFactory(sslSocketFactory,trustManagers[0])
    .build();
,

文档示例不是很好,它没有显示您在不使用自签名证书时可能需要发送的中间证书。

https://github.com/square/okhttp/blob/ef5d0c83f7bbd3a0c0534e7ca23cbc4ee7550f3b/okhttp-tls/README.md

一个更好的例子来自单元测试

https://github.com/square/okhttp/blob/f8fd4d08decf697013008b05ad7d2be10a648358/okhttp/src/test/java/okhttp3/internal/tls/ClientAuthTest.java#L331-L333

builder.heldCertificate(heldCertificate,intermediates);

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?