Istnieje pewien kod w naszym systemie do automatycznego generowania samopodpisanych certyfikatów do magazynu kluczy, który jest następnie wykorzystywany przez Jetty. Jeśli kluczowym dla danego hosta istnieje już potem nic się nie dzieje, ale jeśli nie istnieje, możemy wygenerować nowy klucz, tak:Jeśli jest więcej niż jeden certyfikat w magazynie kluczy Jetty, jak on się wybiera?
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);
}
To wszystko działa w porządku tak długo, jak istnieje tylko jeden klucz i certyfikat w magazynie kluczy. Gdy masz wiele kluczy, dziwne rzeczy dzieją się podczas próby połączenia:
java.io.IOException: HTTPS hostname wrong: should be <127.0.0.1>
To był dość tajemnicze błąd, ale w końcu udało się śledzić go przez pisanie testów jednostkowych, który łączy się z serwerem i twierdzi, że CN na certyfikacie pasuje do nazwy hosta. To, co znalazłem, było całkiem interesujące - Jetty zdaje się arbitralnie wybierać, który certyfikat należy przedstawić klientowi, ale w spójny sposób.
Na przykład:
- Jeśli "CN = localhost" i "CN = cheese.mydomain" znajdują się w magazynie kluczy, to zawsze wybierał "CN = cheese.mydomain".
- Jeśli "CN = 127.0.0.1" i "CN = cheese.mydomain" znajdują się w magazynie kluczy, zawsze wybiera "CN = cheese.mydomain".
- Jeśli "CN = 192.168.222.100" (cheese.mydomain) i "CN = cheese.mydomain" znajdują się w magazynie kluczy, zawsze wybiera "CN = 192.168.222.100".
Napisałem kod, który zapętlał się przez certyfikaty w sklepie, aby je wydrukować i stwierdził, że nie konsekwentnie wybiera pierwszy certyfikat lub coś tak trywialnego.
Jakich dokładnie kryteriów używa? Początkowo myślałem, że localhost jest wyjątkowy, ale potem trzeci przykład mnie kompletnie zaskoczył.
Uważam, że jest to w jakiś sposób określone przez KeyManagerFactory, którym jest SunX509 w moim przypadku.
Tak, odkryliśmy rzecz również z adresami IP, co zaowocowało napisaniem własnego HostnameVerifier, aby sprawdzić, czy pasuje i zwraca true. Pierwotnie mieliśmy HostnameVerifier, który zawsze zwracał wartość true, która po prostu musiała zostać usunięta z oczywistych powodów. Wielu istniejących użytkowników skonfigurowało adres IP jako swoją "nazwę hosta" i nie chcemy jeszcze agresywnie atakować. – Trejkaz