当密钥库有多个服务器证书时,Java 如何选择默认证书

How does Java pick default certificate when keystore has multiple server certificates

当客户端根本没有设置 SNI 服务器名时,Java select 如何使用默认/后备服务器证书?

背景:

我已经使用多个服务器证书创建了 PKCS#12 密钥库。我还使用 JKS 密钥库进行了测试。我使用 NewSunX509 X509KeyManager 的实现,它可以 select 根据请求的 SNI 服务器名匹配服务器证书。

我找不到 JSSE 用来选择默认或“回退”服务器证书的规则,它 returns 当客户端不在 TLS 握手中发送 SNI 服务器名时。我没有找到文档或通过测试弄清楚。实现 (code link) 表示它对不完全匹配进行排序。实际上,selection 似乎受到在密钥库中添加条目的顺序的影响,但它不会简单地选择第一个或最后一个条目,或者按别名排序,即使代码注释给人这样的印象。

我没有看到任何关于按别名排序的评论。 如果 imperfect-match 排序发生,它使用 EntryStatus.compareTo 首先按 CheckResult 排序(即衡量证书与 'good') 然后 keyIndex 这是它在调用者根据 SSL/TLS 协议请求的 keyTypes 列表中的位置(即密码组 and/or signature_algorithm 值,可能按优先顺序排列)。如果它们都相等,Collections.sort 是稳定的,所以它将使用它们被测试和发现的顺序,见下一个。

但是,仅当没有 'perfect' 匹配项时才会进行排序;一旦找到任何 'perfect' 匹配项,它就会返回,而不查找可能存在的任何其他匹配项。因此,返回哪个取决于查看和测试密钥库条目的顺序。 chooseAlias 和它的 with-SNI-idalg 兄弟,如果有多个,首先按 'builder' 顺序查找,通常不会有;在 'builder' 中(即在密钥库中),他们调用 getAliases ,您可以看到它使用 ks.aliases() 返回的 Enumeration -- 这是由 KeyStore实例使用。

File-based 像 PKCS12 和 JKS 这样的密钥库通常使用 Map 或更旧的 (pre-Collection) Hashtable 键控别名。特别是 PKCS12 按插入顺序使用 LinkedHashMap 其中 returns keys/entries,我认为这或多或少是它们在存储文件中存在的顺序(尽管在 PKCS12 中证书和私钥可能是有序的不同,我不确定是哪个控件),但这不需要与 aliases/names、创建或其他任何顺序相同。 JKS 使用 Hashtable 以 hashcode 模数的映射大小的降序枚举,映射大小主要取决于插入的条目数,如果发生冲突,它使用链表和 returns 以相反的插入顺序-- 除非重新散列改变了这一点。

对于 non-file-based 密钥库,如 PKCS11,Windows,Apple 可以通过 Java 接口代码、底层设施提供的内容或组合来确定。

TLDR:就您而言,它是不可预测的,并且出于实际目的也可能是随机的。如果你关心你得到的是哪一个,要么编写你自己的 KeyManager 逻辑来实现你的选择(或者使用像 Apache httpcomponents 这样的预写逻辑)或者过滤你提供给默认 KeyManager 的密钥库中的数据。

另外需要说明的是,这仅适用于您指定的 NewSunX509default KeyManager(仍然!)是较旧的 SunX509,并且工作方式不同;它有自己的 HashMap 来控制迭代,而不是使用底层密钥库的 HashMap。 (单数,因为SunX509只取一个。)