Java SSL选择错误的证书,忽略密钥用法 [英] Java SSL picks wrong cert, ignores key usage

查看:510
本文介绍了Java SSL选择错误的证书,忽略密钥用法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用SSL进行通讯的客户端/服务器。



客户端中的密钥库JKS包含具有不同密钥用法的两个证书。 Cert 1用于SSL通信,cert 2用于签名数据:


  1. 一个证书有 KU = keyEncipherment,digitalSignature ,EKU = clientAuth

  2. 一个证书有 KU = nonRepudiation,digitalSignature

当我运行这个,它选择第一个(正确!)。如果我从JKS中删除第一个证书,它在连接到服务器时选择 nonRepudiation 的证书(错误!?)。



这对我来说没有意义,因为nonRep不应该像afaik一样使用。



我要解决这个问题,我如何使Java SSL只使用正确的密钥使用证书?



Btw,JDK1.70_21在Windows上。我使用的代码是标准的东西。只需获取默认的密钥管理器等

解决方案

首先,您的第二个证书可能有 nonRepudiation 密钥使用扩展,但据我所知,这并不使其无效的客户端证书使用。 TLS规范并没有真正说明客户端的密钥使用,尽管技术上,它使用数字签名(所以你的证书都可以有效)。假设 digitalSignature 确实是必需的(实际上,就我所知),你的证书都有它,所以你的证书都是合适的。



此外, clientAuth 不是键使用扩展,而是扩展的密钥使用扩展(或Netscape扩展)。



其次,默认 X509KeyManager 的作者可以使用扩展密钥使用扩展, c $ c>在JSSE中的实现在完美匹配丢失时选择返回不完美匹配。请参见代码注释

  / * 
*返回符合给定参数的最佳别名。
*我们使用的算法是:
*。扫描所有构建器中的所有别名,顺序为
*。一旦我们找到完美的匹配,返回
*(即与具有适当的密钥用法
*的证书的匹配,并且没有过期)。
*。如果我们没有找到完美的匹配,保持循环并记住
*不完美匹配
*。最后,排序不完美的比赛。我们喜欢过期的证书
*与适当的密钥使用证书与错误的密钥用法。
*返回第一个。
* /
private String chooseAlias(List< KeyType> keyTypeList,Principal [] issuers,


$ b b

因此,当缺少 keyEncipherment,digitalSignature,clientAuth 的证书时,它会回到另一个。



这是一个有争议的选择,但它不一定是一个坏的选择,有一些实现和部署,不一定是100%标准兼容,并且它是方便的能够发送的东西,不管你的CA给予在一天结束时,这不是一个巨大的风险,因为(a)它是真正由你选择你放在你的密钥库和(b)由服务器检查所有情况下的密钥用法。 / p>

在你使用 javax设置的密钥库中放置一个不想用于SSL / TLS使用的证书似乎很奇怪。 net.ssl.keyStore 属性,或者用它构建一个 SSLContext



我不知道为什么你会把它放在这个密钥库,但如果你需要其他证书可用于您的应用程序中的其他,通常更容易将它放在不同的密钥库。



或者,您可以根据需要实现自己的 X509KeyManager 并覆盖其 chooseClientAlias 方法。在这种情况下,我将创建一个类,它将所有调用委托给默认的 X509KeyManager (从 KeyManagerFactory 默认算法并使用您的密钥库初始化),除了 chooseClientAlias ,您可以在返回别名之前检查密钥使用类型。



参考:




I have a client/server which is using SSL to communicate.

The keystore JKS in the client contains two certs with different key usage. Cert 1 is for SSL communication and cert 2 is for signing data:

  1. One cert has KU=keyEncipherment,digitalSignature, EKU=clientAuth
  2. One cert has KU=nonRepudiation,digitalSignature

When I run with this, it picks the first one (correct!). If I remove the first cert from the JKS, it picks the cert with nonRepudiation when connecting to the server (wrong!?).

That makes no sense to me as nonRep should never be used like that afaik.

So, what can I do to fix this, how can I make Java SSL pick only certs with sane key usage?

Btw, JDK1.70_21 on Windows. The code I'm using is standard stuff. Just getting default keymanagers etc

解决方案

Firstly, your second certificate may have the nonRepudiation key usage extension, but as far as I know, this doesn't make it invalid for client-certificate use. The TLS specification doesn't really say much about key usage on the client side, although technically, it uses a digital signature (so both your certs could be valid). Assuming digitalSignature is indeed required (which it is in practise, as far as I know), both your certs have it, so both your certs are suitable. The fact that one also has other key usages is a different matter.

In addition, clientAuth is not a key usage extension, but an extended key usage extension (or a Netscape extension). Your second certificate, lacking any extended key usage extension, leaves any extended key usage possible.

Secondly, the authors of the default X509KeyManager implementation in the JSSE made the choice of returning imperfect matches when perfect matches were missing. See 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,

Hence, when the certificate with keyEncipherment,digitalSignature,clientAuth is missing, it will fall back on the other one.

It's an arguable choice, but it's not necessarily a bad one. There are implementations and deployments out there that are not necessarily 100% standard compliant, and where it's handy to be able to send something regardless of what your CA gave you. At the end of the day, it's not a massive risk since (a) it's really up to you to choose what you put in your keystore and (b) it's up to the server to check the key usage in all cases.

It seems quite odd to put a certificate that you wouldn't want to use for SSL/TLS usage in a keystore you set with the javax.net.ssl.keyStore property, or to build an SSLContext with it.

I'm not sure why you would put it in this keystore, but if you need that other certificate to be available for something else in your application, it's generally easier to place it in a different keystore.

Alternatively, you can implement your own X509KeyManager and override its chooseClientAlias method as required. In this case, I would create a class that delegates all the calls to the default X509KeyManager (obtained from a KeyManagerFactory with the default algorithm and initialised with your keystore), except chooseClientAlias where you'd check the key usage type before returning the alias. It's certainly feasible, but a bit more work than using two keystores.

For reference:

这篇关于Java SSL选择错误的证书,忽略密钥用法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆