W interfejsie API JSSE (część zajmująca się protokołem SSL/TLS) sprawdzenie, czy certyfikat jest zaufany, nie musi obejmować wartości KeyStore
. Dotyczy to w zdecydowanej większości przypadków, ale zakładając, że zawsze będzie jedna, jest niepoprawna. Odbywa się to poprzez TrustManager
. Ten TrustManager
prawdopodobnie użyje domyślnego magazynu zaufanych certyfikatów KeyStore
lub określonego przez właściwość systemową javax.net.ssl.trustStore
, ale niekoniecznie tak jest, a ten plik niekoniecznie jest $JAVA_HOME/jre/lib/security/cacerts
(wszystko to zależy od ustawień zabezpieczeń JRE).
W ogólnym przypadku nie można uzyskać wartości KeyStore
używanej przez menedżera zaufania używanego przez aplikację, a tym bardziej, jeśli domyślny magazyn zaufanych certyfikatów jest używany bez żadnych ustawień właściwości systemu. Nawet jeśli były w stanie dowiedzieć się, co to KeyStore
jest taka, że nadal będzie obliczu dwóch możliwych problemów (co najmniej):
- może nie być w stanie pisać do tego pliku (który niekoniecznie jest problemem jeśli nie zapisujesz zmian na stałe).
- Ten
KeyStore
może nawet nie być oparty na plikach (np. Może to być Keychain na OSX) i może nie mieć dostępu do zapisu również.
Co proponuję to napisać otoki wokół domyślnie TrustManager
(dokładniej: X509TrustManager
), który przeprowadza kontrole przed domyślnego menedżera zaufania, a jeśli ten początkowy test się nie powiedzie, wykonuje wywołania zwrotnego dla użytkownika interfejs aby sprawdzić, czy dodać go do "lokalnego" magazynu zaufanych certyfikatów.
Można to zrobić, jak pokazano w this example (z brief unit test), jeśli chcesz użyć czegoś takiego jak jSSLutils.
Przygotowanie SSLContext
byłoby zrobić tak:
KeyStore keyStore = // ... Create and/or load a keystore from a file
// if you want it to persist, null otherwise.
// In ServerCallbackWrappingTrustManager.CheckServerTrustedCallback
CheckServerTrustedCallback callback = new CheckServerTrustedCallback {
public boolean checkServerTrusted(X509Certificate[] chain,
String authType) {
return true; // only if the user wants to accept it.
}
}
// Without arguments, uses the default key managers and trust managers.
PKIXSSLContextFactory sslContextFactory = new PKIXSSLContextFactory();
sslContextFactory
.setTrustManagerWrapper(new ServerCallbackWrappingTrustManager.Wrapper(
callback, keyStore));
SSLContext sslContext = sslContextFactory.buildSSLContext();
SSLSocketFactory sslSocketFactory = sslContext.getSslSocketFactory();
// ...
// Use keyStore.store(...) if you want to save the resulting keystore for later use.
(Oczywiście, nie trzeba korzystać z tej biblioteki i jej SSLContextFactory
, ale zaimplementować własną X509TrustManager
, „opakowanie”, czy nie domyślny jeden, jak wolisz.)
Innym czynnikiem, który musisz wziąć pod uwagę, jest interakcja użytkownika z tym oddzwanianiem. Do czasu, gdy użytkownik podjął decyzję o kliknięciu przycisku akceptuj lub odrzuć (na przykład), może upłynąć limit czasu uzgadniania, więc może być konieczna ponowna próba połączenia, gdy użytkownik zaakceptuje certyfikat.
Inną kwestią, którą należy wziąć pod uwagę przy projektowaniu wywołania zwrotnego, jest to, że menedżer zaufania nie wie, które gniazdo jest używane (w przeciwieństwie do jego odpowiednika X509KeyManager
), więc nie powinno być tak mało dwuznaczności co do działania użytkownika to wyskakujące okienko (lub jakkolwiek chcesz zaimplementować wywołanie zwrotne). Jeśli wykonano wiele połączeń, nie chciałbyś potwierdzić niewłaściwego. Wydaje się, że możliwe jest rozwiązanie tego problemu za pomocą odrębnego wywołania zwrotnego, SSLContext i SSLSocketFactory na SSLSocket, który powinien utworzyć nowe połączenie, pewien sposób związania SSLSocket i wywołania zwrotnego z działaniem podjętym przez użytkownika w celu wywołania tej próby połączenia w pierwsze miejsce.
To może być trochę za późno, ale http://jcalcote.wordpress.com/2010/06/22/managing-a-dynamic-java-trust-store/ ten link pomaga. – goh