最终成功解决了所有问题,所以我会回答自己的问题。这些是我用来解决我的特定问题的设置/文件;
客户端密钥库 是一个包含 PKCS#12格式 的文件,其中包含:
- 客户端的公共证书(此实例中由自签名 CA 签名)
- 客户端的私有密钥
我使用 OpenSSL 的 pkcs12
命令生成它,例如;
openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12 -name "Whatever"
提示: 请确保您获取了最新的OpenSSL版本,而不是0.9.8h版本,因为该版本似乎存在一个错误,无法正确生成PKCS#12文件。
这个PKCS#12文件将被Java客户端用来在服务器明确要求客户端进行身份验证时向服务器提供客户端证书。有关客户端证书认证协议实际如何工作的概述,请参阅维基百科TLS文章(同时解释了为什么我们需要在这里使用客户端的私钥)。
客户端信任库(truststore)是一个包含根证书或中间CA证书的直接JKS格式文件。这些CA证书将决定您可以与哪些端点通信,在此情况下,它将允许您的客户端连接到由信任库中的一个CA签名的证书的任何服务器。
您可以使用标准的Java keytool来生成它,例如;
keytool -genkey -dname "cn=CLIENT" -alias truststorekey -keyalg RSA -keystore ./client-truststore.jks -keypass whatever -storepass whatever
keytool -import -keystore ./client-truststore.jks -file myca.crt -alias myca
使用此信任库,您的客户端将尝试与所有提供由myca.crt
标识的 CA 签名证书的服务器进行完整的 SSL 握手。
上面的文件严格仅适用于客户端。当您还想要设置服务器时,服务器需要其自己的密钥和信任库文件。有一个很好的详细步骤,可以为 Java 客户端和服务器(使用 Tomcat)设置一个完全可工作的示例,在此网站上可以找到。
问题/备注/提示
- 只有服务器才能强制要求 客户端证书认证。
- (重要!) 当服务器请求客户端证书(作为 TLS 握手的一部分)时,它还将提供可信的 CA 列表作为证书请求的一部分。当您希望用于身份验证的客户端证书未由这些 CA 之一签名时,它根本不会被呈现(在我看来,这是奇怪的行为,但我相信有理由)。这是我的主要问题的原因,因为另一方未正确配置其服务器以接受我自签名的客户端证书,而我们认为问题出在我这里,因为我没有在请求中正确提供客户端证书。
- 获取 Wireshark。它具有出色的 SSL/HTTPS 包分析功能,并将在调试和查找问题时提供巨大帮助。它类似于
-Djavax.net.debug=ssl
,但更结构化且(可以说)更容易理解,如果您对 Java SSL 调试输出感到不适,则可以使用它。
完全可以使用 Apache HttpClient 库。如果您想要使用 httpclient,请将目标 URL 替换为 HTTPS 等效项,并添加以下 JVM 参数(对于任何其他客户端都是相同的,无论您要使用哪个库来发送/接收 HTTP/HTTPS 数据):
-Djavax.net.debug=ssl
-Djavax.net.ssl.keyStoreType=pkcs12
-Djavax.net.ssl.keyStore=client.p12
-Djavax.net.ssl.keyStorePassword=whatever
-Djavax.net.ssl.trustStoreType=jks
-Djavax.net.ssl.trustStore=client-truststore.jks
-Djavax.net.ssl.trustStorePassword=whatever