如果Jetty的密钥库中有多个证书,它会如何选择?

时间:2012-05-03 04:50:24

标签: java ssl certificate jetty

我们的系统中有一些代码可以自动生成自签名证书到密钥存储区,然后由Jetty使用。如果给定主机的密钥已经存在,则没有任何反应,但如果它不存在,我们会生成一个新密钥,如下所示:

public void generateKey(String commonName) {
    X500Name x500Name = new X500Name("CN=" + commonName);
    CertAndKeyGen keyPair = new CertAndKeyGen("DSA", "SHA1withDSA");
    keyPair.generate(1024);
    PrivateKey privateKey = keyPair.getPrivateKey();
    X509Certificate certificate = keyPair.getSelfCertificate(x500Name, 20*365*24*60*60);
    Certificate[] chain = { certificate };
    keyStore.setEntry(commonName, privateKey, "secret".toCharArray(), chain);
}

只要密钥库中只有一个密钥和证书,这一切都可以正常工作。一旦你有多个密钥,当你尝试连接时会发生奇怪的事情:

java.io.IOException: HTTPS hostname wrong:  should be <127.0.0.1>

这是一个非常神秘的错误,但我终于设法通过编写连接到服务器的单元测试来跟踪它,并断言证书上的CN与主机名匹配。我发现的内容非常有趣 - Jetty似乎随意选择向客户提供哪种证书,但是以一致的方式。

例如:

  • 如果“CN = localhost”和“CN = cheese.mydomain”在密钥库中,它总是选择“CN = cheese.mydomain”。
  • 如果“CN = 127.0.0.1”和“CN = cheese.mydomain”在密钥库中,它总是选择“CN = cheese.mydomain”。
  • 如果“CN = 192.168.222.100”(cheese.mydomain)和“CN = cheese.mydomain”在密钥库中,它总是选择“CN = 192.168.222.100”。

我写了一些代码,这些代码循环遍历商店中的证书以打印出来,并发现它并不是一直选择第一个证书或类似的任何小事。

它究竟使用什么标准?最初我认为localhost很特别,但第三个例子完全让我感到困惑。

我认为这是由KeyManagerFactory以某种方式决定的,在我的情况下是SunX509。

1 个答案:

答案 0 :(得分:15)

这确实最终由KeyManager(通常从KeyManagerFactory获得)确定。

密钥库可以在不同的别名下存储许多证书。如果没有通过certAlias in the Jetty configuration显式配置别名,SunX509实现将选择它找到的第一个别名,其中包含私钥和所选密码套件的正确类型的密钥(通常为RSA,但在这种情况下可能是DSA)。如果你看一下Sun provider implementation,那么选择逻辑会有更多的东西,但你不应该真正依赖顺序,只需要别名。

您当然可以使用自己的X509KeyManager为Jetty提供自己的SSLContext来选择别名。你必须实现:

 chooseServerAlias(String keyType, Principal[] issuers, Socket socket)

不幸的是,除了keyTypeissuers之外,你所做出的所有决定都是socket本身。充其量,您获得的有用信息是本地IP地址和远程IP地址。

除非您的服务器在同一端口上侦听多个IP地址,否则您将始终获得相同的本地IP地址。 (这里,显然,你至少有两个:127.0.0.1192.168.222.100,但我怀疑你对localhost不感兴趣,除了你自己的测试。)你需要服务器名称指示(SNI)支持服务器端,以便能够根据请求的主机名(由支持它的客户端)做出决定。不幸的是,SNI was only introduced in Java 7, but only on the client side

您将面临的另一个问题是Java clients will complain about IP addresses in the Subject DN's CN。有些浏览器会容忍这种情况,但这不符合HTTPS规范(RFC 2818)。 IP地址必须是IP地址类型的主题备用名称条目。