在大多数企业环境中,在应用程序之间的连接中使用某种形式的安全通信(例如TLS或SSL)。 在某些环境中, 相互(双向)身份验证也是一项非功能性要求。 有时将其称为双向SSL或双向TLS身份验证。 因此,除了提供证书的服务器外,它还请求客户端发送其证书,以便随后可以使用它来认证调用方。
我当前客户的一个合作伙伴正在开发一种服务器,该服务器可以通过MQTT接收数据,并且由于数据非常敏感,因此客户决定应使用相互TLS身份验证来保护数据。 此外,客户要求将该服务器收集的聚合数据发布到其他下游服务时,还必须使用双向TLS身份验证来完成。 该服务器需要向其调用方提供服务器证书,以便他们可以验证主机名和身份,但是另外,当在SSL握手期间被请求这样做时,它必须向下游服务器提供具有有效用户ID的客户端证书。
最初的想法是使用用于配置密钥库的标准JVM系统属性来实现此目的:“-Djavax.net.ssl.keyStore =…”,即将客户端和服务器证书都放入单个密钥库中。 但是,我们很快意识到这是行不通的,并且跟踪SSL调试日志表明,无论是在传入SSL握手期间还是传出SSL握手期间,服务器都提供了错误的证书。 在传入握手期间,它应出示其服务器证书。 在外发握手期间,它应出示其客户证书。
注释了以下日志摘录,并显示了这些问题:
- 提供给呼叫者的证书有误
- 向下游提交了错误的证书
经过进一步调查,很明显该问题与JVM中的默认密钥管理器实现有关。 的
SunX509KeyManagerImpl
类用于选择JVM在握手期间应提供的证书,并且对于客户机证书和服务器证书选择,代码仅采用找到的第一个证书:
String[] aliases = getXYZAliases(keyTypes[i], issuers);if ((aliases != null) && (aliases.length > 0)) {return aliases[0]; <========== NEEDS TO BE MORE SELECTIVE}
第一行中的方法返回的别名仅匹配密钥类型(例如DSA)和可选的颁发者。 因此,在密钥库包含两个或多个证书的情况下,选择性不够。 此外,列表的顺序基于对HashMap条目集的迭代,因此顺序不是说字母顺序的,而是确定性的和恒定的。 因此,在搜索服务器证书时,该算法可能会返回客户端证书。 但是,如果该部分起作用,则当服务器进行下游连接并需要出示其客户端证书时,该算法将失败,因为将再次出示第一个证书,即服务器证书。 因此,由于无法创建并发的传入和传出双向SSL连接,因此我向Oracle提交了一个错误(内部审查ID 9052786在20180225上报告给Oracle)。
一种解决方案是使用两个密钥库,每个证书存储一个, 如此处所示 。
JVM的一个可能补丁是通过使用
“扩展密钥用法”证书扩展。 基本上,可以增强上述代码,以额外检查扩展的密钥用法并在别名选择期间做出更明智的决定,例如:
String[] aliases = getXYZAliases(keyTypes[i], issuers);
if ((aliases != null) && (aliases.length > 0)) {String alias = selectAliasBasedOnExtendedKeyUsage(aliases, "1.3.6.1.5.5.7.3.2"); //TODO replace with constantif (alias != null) return alias;//default as implemented in openjdkreturn aliases[0];
}
选择别名的方法如下:
private String selectAliasBasedOnExtendedKeyUsage(String[] aliases, String targetExtendedKeyUsage) {for(String alias : aliases){//assume cert in index 0 is the lowest one in the chain, and check its EKUX509Certificate certificate = this.credentialsMap.get(alias).certificates[0];List ekus = certificate.getExtendedKeyUsage();for (String eku : ekus) {if(eku.equals(targetExtendedKeyUsage)){return alias;}}}return null;
}
更多详细信息,包括完整运行的示例和单元测试, 请参见此处 。
翻译自: https://www.javacodegeeks.com/2018/03/java-problem-with-mutual-tls-authentication-when-using-incoming-and-outgoing-connections-simultaneously.html