gpt4 book ai didi

spring-boot - 将客户端证书与改造 (okhttp) 一起使用时出现 bad_certificate 错误,但与 curl 一起使用时效果很好

转载 作者:行者123 更新时间:2023-12-04 22:36:19 27 4
gpt4 key购买 nike

我在使用改造、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 客户端变量传递给 retrofit。当服务器和客户端证书都是自签名时,完全相同的代码工作正常,但现在它不适用于 CA 签名证书。

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

我正在使用 retrofit 2.4.0、okhttp-tls 3.14.6、java 8,我将无法升级。

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

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

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

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 签名)。

最佳答案

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

我将根据您当前的设置提供此示例,因此请记住所有内容都存储在一个 keystore 文件中。我还注意到您使用了 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();

关于spring-boot - 将客户端证书与改造 (okhttp) 一起使用时出现 bad_certificate 错误,但与 curl 一起使用时效果很好,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67064309/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com