首先,您的第二个证书可能具有nonRepudiation 密钥使用扩展名,但据我所知,这不会使其对客户端证书使用无效。 TLS 规范并没有真正说明客户端的密钥使用情况,尽管从技术上讲,它使用数字签名(因此您的两个证书都可能有效)。假设确实需要digitalSignature(据我所知,这是在实践中),你的两个证书都有它,所以你的两个证书都是合适的。一个人还有其他关键用途这一事实是另一回事。
此外,clientAuth 不是密钥使用扩展,而是扩展 密钥使用扩展(或 Netscape 扩展)。您的第二个证书缺少任何扩展密钥使用扩展,因此可以使用任何扩展密钥。
其次,JSSE 中默认的X509KeyManager 实现的作者选择在缺少完美匹配时返回不完美匹配。见code comments:
/*
* Return the best alias that fits the given parameters.
* The algorithm we use is:
* . scan through all the aliases in all builders in order
* . as soon as we find a perfect match, return
* (i.e. a match with a cert that has appropriate key usage
* and is not expired).
* . if we do not find a perfect match, keep looping and remember
* the imperfect matches
* . at the end, sort the imperfect matches. we prefer expired certs
* with appropriate key usage to certs with the wrong key usage.
* return the first one of them.
*/
private String chooseAlias(List<KeyType> keyTypeList, Principal[] issuers,
因此,当带有keyEncipherment,digitalSignature,clientAuth 的证书丢失时,它将退回到另一个。
这是一个有争议的选择,但不一定是一个坏选择。有些实现和部署不一定 100% 符合标准,并且无论您的 CA 给您什么,都可以方便地发送一些东西。归根结底,这并不是一个巨大的风险,因为 (a) 您可以选择放入密钥库中的内容,并且 (b) 在所有情况下都由服务器检查密钥使用情况。
将一个您不想用于 SSL/TLS 的证书放在您使用 javax.net.ssl.keyStore 属性设置的密钥库中,或者用它构建一个 SSLContext 似乎很奇怪。
我不确定您为什么要将它放在此密钥库中,但如果您需要其他证书可用于您的应用程序中的其他内容,通常将其放在不同的密钥库中更容易。
或者,您可以实现自己的X509KeyManager 并根据需要覆盖其chooseClientAlias 方法。在这种情况下,我将创建一个将所有调用委托给默认 X509KeyManager 的类(从具有默认算法的 KeyManagerFactory 获得并使用您的密钥库初始化),除了 chooseClientAlias 您将在其中检查密钥使用情况在返回别名之前输入。这当然是可行的,但比使用两个密钥库要多一些工作。
供参考: