2011-06-23 11 views
7

Jestem nieco zdezorientowany, próbując użyć HttpClient do wywołania strony https, która używa samopodpisanego certyfikatu. Mam poniższy kod, który pozwala mi nawiązać połączenie, ale otrzymuję komunikat o błędzie: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: No trusted certificate found Pobrałem certyfikat z mojej przeglądarki internetowej i rozumiem, że mogę zaimportować go do magazynu kluczy, ale wolałbym go po prostu wstawić do kodu i używać go w ten sposób, czy istnieje sposób, aby to zrobić?Błąd Java HttpClient dla znalezionego certyfikatu SSL, używając certyfikatu jako ciągu w kodzie?

HttpClient client = new HttpClient(); 

    EasySSLProtocolSocketFactory easySSLProtocolSocketFactory = new EasySSLProtocolSocketFactory(); 
    Protocol https = new Protocol("https", easySSLProtocolSocketFactory, 
      443); 
    Protocol.registerProtocol("https", https); 

    BufferedReader br = null; 

    String responseString = ""; 

    GetMethod method = new GetMethod(path); 

    int returnCode = client.executeMethod(method); 
+0

Dlaczego, na Boga, umieściłbyś go w kodzie? Co się stanie, gdy certyfikat serwera wygaśnie i otrzyma nowy? Nie rób tego. – EJP

Odpowiedz

4

Założenie, że twój certyfikat jest w formacie PEM. Możesz go osadzić w kodzie i użyć BouncyCastle 's PEMReader, aby przekształcić go w instancję X509Certificate. Gdy to zrobisz, utwórz instancję KeyStore w pamięci i umieść w niej ten certyfikat X.509. Następnie wykonaj instancję nowego SSLContext, korzystając z tej KeyStore, jako magazynu zaufanych certyfikatów i spraw, aby klient HTTP z niego korzystał.

to będzie wyglądać następująco (nie próbowałem, należy pamiętać, aby zamknąć czytelników i złapać wyjątki ...):

PEMReader pemReader = new PEMReader(new StringReader("----- BEGIN ......"); 
X509Certificate cert = (X509Certificate) pemReader.readObject(); 

KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); 
ks.load(null, null); 
ks.setCertificateEntry("some name", cert); 

TrustManagerFactory tmf = TrustManagerFactory.getInstance(
    TrustManagerFactory.getDefaultAlgorithm()); 
tmf.init(ks); 

SSLContext sc = SSLContext.getInstance("TLS"); 
sc.init(null, tmf.getTrustManagers(), null); 

Następnie użyj tego SSLContext dla połączenia. Możesz to zrobić za pomocą Apache HttpClient's SSLSocketFactory, jeśli używasz wersji 4.x (lub this, jeśli używasz wersji 3.x). Sugerowałbym obecnie używanie Apache HttpClient 4.x.

+0

dziękuję, to jest świetna informacja, nie jestem zaznajomiony z SSLSocketFactory i staram się znaleźć przykład, zgaduję, że to zastąpi 'EasySSLProtocolSocketFactory' w moim kodzie? Wszelkie porady dotyczące korzystania z tego są bardzo mile widziane – Rick

+0

rzeczywiście Myślę, że zaimportowałem niewłaściwą fabrykę SSL (z Java.net.ssl ​​.. Widzę, że potrzebuję teraz Apache – Rick

+0

czy mógłbyś wyjaśnić który pakiet SSLContext, którego używasz, przepraszam jestem tylko nieco zdezorientowany, ponieważ jest więcej niż 1 – Rick

0

ten sposób, aby Apache HttpClient 4.3, które akceptują samopodpisanym certyfikaty:

HttpClientBuilder cb = HttpClientBuilder.create(); 
SSLContextBuilder sslcb = new SSLContextBuilder(); 
sslcb.loadTrustMaterial(KeyStore.getInstance(KeyStore.getDefaultType()), 
         new TrustSelfSignedStrategy()); 
cb.setSslcontext(sslcb.build()); 
HttpClient client = cb.build() 

Teraz aby wykonać test POST lub GET żądania używać standardowych wykonać metodą:

HttpResponse response = client.execute(...); 

Przypomnienie: Jesteś podatny gdy ufasz certyfikatowi z podpisem własnym.

+0

Uzyskiwanie tego wyjątku, kiedy używam twojego code: "Wyjątek w wątku" główny "javax.net.ssl.SSLException: java.lang.RuntimeException: nieoczekiwany błąd: java.security.InvalidAlgorithmParameterException: parametr trustAnchors musi być niepusty" – Mike

+0

Akceptowanie wszystkich certyfikatów z podpisem własnym [nie jest bezpieczne] (http://stackoverflow.com/q/23923810/781723); w pierwszej kolejności całkowicie pokonuje cel używania SSL. –

2

Opierając się na odpowiedź przez Alexander Chzhen i HttpClient 4.3 I najpierw utworzyć kontekst, który ufa wszystkim:

SSLContextBuilder sslctxb = new SSLContextBuilder(); 

sslctxb.loadTrustMaterial(KeyStore.getInstance(KeyStore.getDefaultType()), 
          new TrustSelfSignedStrategy() { 
    @Override 
    public boolean isTrusted(X509Certificate[] chain, 
          String   authType) 
    throws CertificateException { 
    return true; 
    } 
}); 

SSLContext sslctx = sslctxb.build(); 

wówczas wykonawca klient:

HttpClientBuilder hcb = HttpClients.custom(); 

a ja tylko ustawić kontekst. Nie używam setSSLSocketFactory ponieważ będzie to kolidować z setHostnameVerifier poniżej:

hcb.setSslcontext(sslctx); 

Wreszcie mogę ustawić weryfikatora hosta, który weryfikuje wszystkie:

hcb.setHostnameVerifier(new X509HostnameVerifier() { 
    @Override 
    public void verify(String host, SSLSocket ssl) 
    throws IOException { 
    } 

    @Override 
    public void verify(String host, X509Certificate cert) 
    throws SSLException { 
    } 

    @Override 
    public void verify(String host, String[] cns, String[] subjectAlts) 
    throws SSLException { 
    } 

    @Override 
    public boolean verify(String hostname, SSLSession session) { 
    return true; 
    } 
}); 

wreszcie zbudować klienta:

HttpClient c = hcb.build(); 
+0

+1, ponieważ działa to, gdy rozwiązanie Alexandra nie ma wartości – Mike

+1

@Mike, problem z tymi dwoma rozwiązaniami polega na tym, że akceptujesz * dowolny * samopodpisany certyfikat (który jest mniej więcej taki sam, jak całkowite wyłączenie weryfikacji certyfikatu). – Bruno

1

Jeśli chcesz zaakceptować tylko ten pojedynczy certyfikat, ale nie wszystkie certyfikaty z podpisem własnym, powinieneś pobrać certyfikat i przechowywać plik pem tutaj.

Teraz możesz użyć tego kodu do załadowania pliku pem, utworzenia nowego magazynu zaufanych certyfikatów i użycia magazynu zaufanych certyfikatów dla swojego HttpClient.

//use java. ... .X509Certificate, not javax. ... .X509Certificate 
import java.security.cert.X509Certificate; 
import java.security.cert.CertificateException; 
import java.security.cert.CertificateFactory; 

@Test 
public void testSslCertificate() 
     throws IOException, KeyStoreException, NoSuchAlgorithmException, 
       CertificateException, KeyManagementException { 

    X509Certificate cert; 
    try (FileInputStream pemFileStream = new FileInputStream(newFile("your.pem"))) { 
     CertificateFactory certFactory = CertificateFactory.getInstance("X509"); 
     cert = (X509Certificate) certFactory.generateCertificate(pemFileStream); 
    } 

    //create truststore 
    KeyStore trustStore = KeyStore.getInstance("JKS"); 
    trustStore.load(null); //create an empty trustore 

    //add certificate to truststore - you can use a simpler alias 
    String alias = cert.getSubjectX500Principal().getName() + "[" 
      + cert.getSubjectX500Principal().getName().hashCode() + "]"; 
    trustStore.setCertificateEntry(alias, cert); 

    //configure http client 
    TrustManagerFactory trustManagerFactory = 
     TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 
    trustManagerFactory.init(trustStore); 

    SSLContext sslContext = SSLContext.getInstance("TLS"); 
    sslContext.init(null, trustManagerFactory.getTrustManagers(), null); 

    HttpClientBuilder httpClientBuilder = HttpClientBuilder. 
              create().setSslcontext(sslContext); 

    try (CloseableHttpClient httpClient = httpClientBuilder.build()) { 
     HttpGet httpGetRequest = new HttpGet("https://yourServer"); 
     try (CloseableHttpResponse httpResponse = 
          httpClient.execute(httpGetRequest)) { 
      Assert.assertEquals(200, 
           httpResponse.getStatusLine().getStatusCode()); 
     } 
    } 
} 
0

W wielu sytuacjach przypinanie certyfikatów może być lepsze niż kodowanie konkretnego certyfikatu.

Tak, możesz kodować certyfikat w swoim kodzie, a to zadziała i będzie bezpieczne. To całkowicie rozsądne podejście. Ma jednak pewne wady. Jedną z pułapek jest to, że w końcu ten certyfikat wygaśnie, a wtedy Twoja aplikacja przestanie działać. Ponadto, jeśli kiedykolwiek będziesz chciał zmienić swój klucz prywatny, nie będziesz w stanie.

Dlatego w wielu sytuacjach używanie przypinania certyfikatów jest bardziej elastyczne i może być lepsze. Zobacz here, aby uzyskać informacje na temat implementowania przypinania certyfikatów.

Powiązane problemy