2012-05-03 29 views
16

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.

Odpowiedz

13

Jest to ostatecznie ostatecznie określone przez KeyManager (zazwyczaj uzyskane z KeyManagerFactory).

Magazyn kluczy może mieć wiele certyfikatów przechowywanych pod różnymi aliasami. Jeśli żaden alias nie zostanie jawnie skonfigurowany za pomocą certAlias in the Jetty configuration, implementacja SunX509 wybierze pierwsze znalezione aliasy, dla których istnieje klucz prywatny i klucz odpowiedniego typu dla wybranego zestawu algorytmów szyfrowania (zazwyczaj RSA, ale prawdopodobnie w tym przypadku DSA) . Jest trochę więcej do wyboru logiki, jeśli spojrzysz na Sun provider implementation, ale nie powinieneś polegać na ogólnej kolejności, tylko na aliasie.

Możesz oczywiście dać Jetty swój własny SSLContext z własnym X509KeyManager, aby wybrać alias. Trzeba by wdrożyć:

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

Niestety, oprócz keyType i issuers, wszystko można dostać się do podjęcia decyzji jest sama socket. W najlepszym razie przydatne informacje, które uzyskasz, to lokalny adres IP i zdalny.

Jeśli twój serwer nie słucha wielu adresów IP na tym samym porcie, zawsze otrzymasz ten sam lokalny adres IP.(Tutaj, oczywiście, masz co najmniej dwa: 127.0.0.1 i 192.168.222.100, ale podejrzewam, że nie jesteś naprawdę zainteresowany localhost, z wyjątkiem twoich własnych testów.) Potrzebujesz wsparcia Server Name Indication (SNI) po stronie serwera, aby móc aby podjąć decyzję na podstawie żądanych nazw hostów (przez klientów, którzy ją obsługują). Niestety, SNI was only introduced in Java 7, but only on the client side.

Innym problemem, który napotkasz tutaj, jest Java clients will complain about IP addresses in the Subject DN's CN. Niektóre przeglądarki będą to tolerować, ale nie jest to zgodne ze specyfikacją HTTPS (RFC 2818). Adresy IP muszą być pozycjami Alternatywna nazwa podmiotu o typie adresu IP.

+0

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

Powiązane problemy