В нашей системе есть некоторый код для автоматического создания самозаверяющих сертификатов в хранилище ключей, которое затем используется 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>
Это была довольно загадочная ошибка, но я, наконец, смог ее отследить, написав unit test, который подключается к серверу, и утверждает, что 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 в моем случае.