我正在尝试连接到安全的网络服务。
即使我的 keystore 和信任库已正确设置,我也遇到了握手失败。
经过几天的挫折,无休止的谷歌搜索和询问周围的每个人,我发现唯一的问题是java在握手期间选择不将客户端证书发送到服务器。
具体来说:
- 服务器请求客户端证书 (CN=RootCA) - 即“给我一个由根 CA 签名的证书”
- Java 查看了 keystore ,只找到了我的客户端证书,该证书由“SubCA”签名,而该证书又由“RootCA”颁发。它没有费心去查看信任库...嗯,我猜是的
- 遗憾的是,当我尝试将“SubCA”证书添加到 keystore 时,这根本没有帮助。我确实检查了证书是否已加载到 keystore 中。确实如此,但 KeyManager 会忽略除客户端之外的所有证书。
- 以上所有情况都导致 java 认为它没有任何证书可以满足服务器的请求并且什么也不发送...tadaaa 握手失败:-(
我的问题:
- 我是否有可能以“破坏证书链”或其他方式将“SubCA”证书添加到 keystore ,以便 KeyManager 仅加载客户端证书而忽略其余证书? (Chrome 和 openssl 设法弄清楚了,为什么 java 不能呢? - 请注意,“SubCA”证书始终作为受信任的权威单独呈现,因此 Chrome 显然在握手期间正确地将其与客户端证书一起打包)
- 这是服务器端的正式“配置问题”吗?服务器是第三方。我希望服务器请求由“SubCA”机构签署的证书,因为这是他们为我们提供的。我怀疑这在 Chrome 和 openssl 中有效的事实是因为它们“限制较少”,而 java 只是“按部就类”并且失败了。
我确实设法为此制定了一个肮脏的解决方法,但我对此不太满意,所以如果有人能为我澄清这一点,我会很高兴。
最佳答案
您可能已将中间 CA 证书导入 keystore ,但未将其与您拥有客户端证书及其私钥的条目相关联。您应该能够使用 keytool -v -list -keystore store.jks
看到这一点。如果每个别名条目仅获得一个证书,则它们不在一起。
您需要将您的证书及其链一起导入到拥有您的私钥的 keystore 别名中。
要找出哪个 keystore 别名具有私钥,请使用 keytool -list -keystore store.jks
(我在这里假设 JKS 存储类型)。这会告诉你这样的事情:
Your keystore contains 1 entry
myalias, Feb 15, 2012, PrivateKeyEntry,
Certificate fingerprint (MD5): xxxxxxxx
这里的别名是myalias
。如果除此之外还使用 -v
,您应该会看到 Alias Name: myalias
。
如果您还没有单独拥有它,请从 keystore 中导出您的客户端证书:
keytool -exportcert -rfc -file clientcert.pem -keystore store.jks -alias myalias
这应该会给你一个 PEM 文件。
使用文本编辑器(或 cat
),使用该客户端证书和中间 CA 证书(可能还有根证书)准备文件(我们称之为 bundle.pem
) CA 证书本身(如果需要),因此客户端证书位于开头,其颁发者证书位于下方。
这应该看起来像:
-----BEGIN CERTIFICATE-----
MIICajCCAdOgAwIBAgIBAjANBgkqhkiG9w0BAQUFADA7MQswCQYDVQQGEwJVSzEa
....
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICkjCCAfugAwIBAgIJAKm5bDEMxZd7MA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNV
....
-----END CERTIFICATE-----
现在,将此 bundle 重新导入到您的私钥所在的别名中:
keytool -importcert -keystore store.jks -alias myalias -file bundle.pem
关于java - 为什么 java 在 SSL 握手期间不发送客户端证书?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9299133/