如果 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。