2014-04-17 29 views
8

Moja aplikacja ma osobisty magazyn kluczy zawierający zaufane certyfikaty z podpisem własnym do użytku w sieci lokalnej - na przykład mykeystore.jks. Chcę mieć możliwość łączenia się z witrynami publicznymi (np. Google.com), a także z witrynami w mojej sieci lokalnej za pomocą certyfikatów z podpisem własnym, które zostały udostępnione lokalnie.Jak zainicjować TrustManagerFactory z wieloma źródłami zaufania?

Problemem tutaj jest to, że po podłączeniu do https://google.com, budowa ścieżki nie powiedzie się, ponieważ ustawienie własne kluczy przesłania domyślny kluczy zawierający głównych urzędów certyfikacji w pakiecie z JRE, zgłoszenie wyjątku

sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target 

Jednakże, jeśli Importuję certyfikat CA do mojego własnego magazynu kluczy (mykeystore.jks), który działa poprawnie. Czy istnieje sposób na wsparcie obu?

mam własną TrustManger tym celu

public class CustomX509TrustManager implements X509TrustManager { 

     X509TrustManager defaultTrustManager; 

     public MyX509TrustManager(KeyStore keystore) { 
       TrustManagerFactory trustMgrFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 
       trustMgrFactory.init(keystore); 
       TrustManager trustManagers[] = trustMgrFactory.getTrustManagers(); 
       for (int i = 0; i < trustManagers.length; i++) { 
        if (trustManagers[i] instanceof X509TrustManager) { 
         defaultTrustManager = (X509TrustManager) trustManagers[i]; 
         return; 
        } 
       } 

     public void checkServerTrusted(X509Certificate[] chain, String authType) 
       throws CertificateException { 
      try { 
       defaultTrustManager.checkServerTrusted(chain, authType); 
      } catch (CertificateException ce) { 
      /* Handle untrusted certificates */ 
      } 
     } 
    } 

Następnie zainicjować SSLContext, TrustManager [] trustManagers = nowy TrustManager [] {nowy CustomX509TrustManager (kluczy)}; SSLContekst customSSLContext = SSLContext.getInstance ("TLS"); customSSLContext.init (null, trustManagers, null);

i ustawienia fabryczne gniazdo,

HttpsURLConnection.setDefaultSSLSocketFactory(customSSLContext.getSocketFactory()); 

Program główny,

URL targetServer = new URL(url); 
HttpsURLConnection conn = (HttpsURLConnection) targetServer.openConnection(); 

Jeśli nie ustawić własne menedżerów Zaufanie, to łączy się https://google.com dobrze. Jak uzyskać "domyślny menedżer zaufania", który wskazuje na domyślny magazyn kluczy?

+0

Możliwy duplikat [Rejestrowanie wielu magazynów kluczy w JVM] (http://stackoverflow.com/questions/1793979/registering-multiple-keystores-in-jvm) – OrangeDog

Odpowiedz

-1

Po zainicjowaniu SSLContextu dostarczasz tablicę TrustManagers. Dostarczasz dwa: domyślny, który używa magazynu zaufanych certyfikatów JRE, i inny, który używa twojego. Model delegacji jest tutaj błędną odpowiedzią.

+0

Niestety, nie ma sposobu, aby uzyskać domyślny menedżer zaufania (korzystający z JRE CA) używany przez JSSE, a jedynie fabrykę gniazd. Mogę wdrożyć własnego menedżera zaufania, który wyszukuje certyfikaty JRE, ale nie sądzę, że jest to czyste rozwiązanie, ponieważ zależy to od lokalizacji JRE. – varrunr

+1

Wykorzystywane jest tylko pierwsze wystąpienie określonego typu implementacji klucza i/lub menedżera zaufania w tablicy. (Na przykład zostanie użyty tylko pierwszy element javax.net.ssl.X509KeyManager w tablicy). – Dale

14

Konfigurujesz defaultTrustManager na własny osobisty magazyn kluczy, a nie na domyślny system plików.

podstawie odczytu kodu źródłowego sun.security.ssl.TrustManagerFactoryImpl, wygląda trustMgrFactory.init((KeyStore) null); zrobi dokładnie to, czego potrzebujesz (załadowanie domyślnego systemu kluczy), a na podstawie szybkiego testu, wydaje się działać dla mnie.

+1

Próbowałem już z wersją Java 1.8 (kompilacja 1.8.0_60-b27). Ale nie zadziałało to dla mnie. Mam ten sam błąd: Budowanie ścieżki PKIX nie powiodło się. –

+0

Nie pozwala to jednak na używanie certyfikatów w niestandardowym magazynie kluczy, a jedynie certyfikaty urzędu certyfikacji zainstalowane w systemie. –

+0

Kudos za znalezienie tego przez czytanie źródła! Upraszcza to wiele ścieżek cacerts i żonglerki obsługi nazw! Zadziwia mnie też, że nie ma wymogu hasła również. – FUD

1

Wystąpiłem w tym samym problemie z Commons HttpClient. rozwiązanie moim przypadku działa było stworzenie łańcucha Delegacja do pkix TrustManagers w następujący sposób:

public class TrustManagerDelegate implements X509TrustManager { 
    private final X509TrustManager mainTrustManager; 
    private final X509TrustManager trustManager; 
    private final TrustStrategy trustStrategy; 

    public TrustManagerDelegate(X509TrustManager mainTrustManager, X509TrustManager trustManager, TrustStrategy trustStrategy) { 
     this.mainTrustManager = mainTrustManager; 
     this.trustManager = trustManager; 
     this.trustStrategy = trustStrategy; 
    } 

    @Override 
    public void checkClientTrusted(
      final X509Certificate[] chain, final String authType) throws CertificateException { 
     this.trustManager.checkClientTrusted(chain, authType); 
    } 

    @Override 
    public void checkServerTrusted(
      final X509Certificate[] chain, final String authType) throws CertificateException { 
     if (!this.trustStrategy.isTrusted(chain, authType)) { 
      try { 
       mainTrustManager.checkServerTrusted(chain, authType); 
      } catch (CertificateException ex) { 
       this.trustManager.checkServerTrusted(chain, authType); 
      } 
     } 
    } 

    @Override 
    public X509Certificate[] getAcceptedIssuers() { 
     return this.trustManager.getAcceptedIssuers(); 
    } 

} 

i zainicjować HttpClient w następujący sposób (tak to brzydki):

final SSLContext sslContext; 
try { 
    sslContext = SSLContext.getInstance("TLS"); 
    final TrustManagerFactory javaDefaultTrustManager = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 
    javaDefaultTrustManager.init((KeyStore)null); 
    final TrustManagerFactory customCaTrustManager = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 
    customCaTrustManager.init(getKeyStore()); 

    sslContext.init(
     null, 
     new TrustManager[]{ 
      new TrustManagerDelegate(
        (X509TrustManager)customCaTrustManager.getTrustManagers()[0], 
        (X509TrustManager)javaDefaultTrustManager.getTrustManagers()[0], 
        new TrustSelfSignedStrategy() 
      ) 
     }, 
     secureRandom 
    ); 

} catch (final NoSuchAlgorithmException ex) { 
    throw new SSLInitializationException(ex.getMessage(), ex); 
} catch (final KeyManagementException ex) { 
    throw new SSLInitializationException(ex.getMessage(), ex); 
} 

SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext); 

PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(
     RegistryBuilder.<ConnectionSocketFactory>create() 
       .register("http", PlainConnectionSocketFactory.getSocketFactory()) 
       .register("https", sslSocketFactory) 
       .build() 
); 
//maximum parallel requests is 500 
cm.setMaxTotal(500); 
cm.setDefaultMaxPerRoute(500); 

CredentialsProvider cp = new BasicCredentialsProvider(); 
cp.setCredentials(
     new AuthScope(apiSettings.getIdcApiUrl(), 443), 
     new UsernamePasswordCredentials(apiSettings.getAgencyId(), apiSettings.getAgencyPassword()) 
); 

client = HttpClients.custom() 
        .setConnectionManager(cm) 
        .build(); 

W twoim przypadku z prostego HttpsURLConnection ty może dostać się z uproszczonej wersji klasy delegującego:

public class TrustManagerDelegate implements X509TrustManager { 
    private final X509TrustManager mainTrustManager; 
    private final X509TrustManager trustManager; 

    public TrustManagerDelegate(X509TrustManager mainTrustManager, X509TrustManager trustManager) { 
     this.mainTrustManager = mainTrustManager; 
     this.trustManager = trustManager; 
    } 

    @Override 
    public void checkClientTrusted(
      final X509Certificate[] chain, final String authType) throws CertificateException { 
     this.trustManager.checkClientTrusted(chain, authType); 
    } 

    @Override 
    public void checkServerTrusted(
      final X509Certificate[] chain, final String authType) throws CertificateException { 
     try { 
      mainTrustManager.checkServerTrusted(chain, authType); 
     } catch (CertificateException ex) { 
      this.trustManager.checkServerTrusted(chain, authType); 
     } 
    } 

    @Override 
    public X509Certificate[] getAcceptedIssuers() { 
     return this.trustManager.getAcceptedIssuers(); 
    } 

} 
+0

Rozwiązanie jest opisane bardziej szczegółowo tutaj: http://blog.novoj.net/2016/02/29/how-to-make-apache-httpclient-trust- allow-encrypt-certificate-authority/ – Novoj

6

odpowiedź here sposób doszedłem do zrozumienia, jak to zrobić.Jeśli chcesz tylko zaakceptować certyfikaty systemu CA plus niestandardowy magazyn kluczy certyfikatów, uprościliśmy go do jednej klasy, korzystając z kilku wygodnych metod. Pełny kod tutaj:

https://gist.github.com/HughJeffner/6eac419b18c6001aeadb

KeyStore keystore; // Get your own keystore here 
    SSLContext sslContext = SSLContext.getInstance("TLS"); 
    TrustManager[] tm = CompositeX509TrustManager.getTrustManagers(keystore); 
    sslContext.init(null, tm, null); 
+0

Composite trust manager to naprawdę pomocne rozwiązanie. – Kane