HttpClient mit SSL

HTTP-Client mit SSL

1. Überblick

Dieser Artikel zeigt, wie manconfigure the Apache HttpClient 4 with “Accept All” SSL support macht. Das Ziel ist einfach: Verwenden Sie HTTPS-URLs ohne gültige Zertifikate.

Wenn Sie tiefer graben und andere coole Dinge lernen möchten, die Sie mit dem HttpClient tun können, gehen Sie zuthe main HttpClient guide.

Weitere Lektüre:

HTTP-Client-Verbindungsverwaltung

So öffnen, verwalten und schließen Sie Verbindungen mit dem Apache HttpClient 4.

Read more

Erweiterte HttpClient-Konfiguration

HTTP-Client-Konfigurationen für erweiterte Anwendungsfälle.

Read more

So senden Sie benutzerdefinierte Cookies mit dem Apache HttpClient 4.

Read more

2. DieSSLPeerUnverifiedException

Ohne die Konfiguration von SSL mitHttpClient schlägt der folgende Test fehl, bei dem eine HTTPS-URL verwendet wird:

public class RestClientLiveManualTest {

    @Test(expected = SSLPeerUnverifiedException.class)
    public void whenHttpsUrlIsConsumed_thenException()
      throws ClientProtocolException, IOException {

        CloseableHttpClient httpClient = HttpClients.createDefault();
        String urlOverHttps
          = "https://localhost:8082/httpclient-simple";
        HttpGet getMethod = new HttpGet(urlOverHttps);

        HttpResponse response = httpClient.execute(getMethod);
        assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
    }
}

Der genaue Fehler ist:

javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
    at sun.security.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:397)
    at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:126)
    ...

javax.net.ssl.SSLPeerUnverifiedException exception tritt immer dann auf, wenn keine gültige Vertrauenskette für die URL hergestellt werden konnte.

3. SSL konfigurieren - Alle akzeptieren (HttpClient <4.3)

Konfigurieren Sie nun den HTTP-Client so, dass er allen Zertifikatketten unabhängig von ihrer Gültigkeit vertraut:

@Test
public final void givenAcceptingAllCertificates_whenHttpsUrlIsConsumed_thenOk()
  throws GeneralSecurityException {
    HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
    CloseableHttpClient httpClient = (CloseableHttpClient) requestFactory.getHttpClient();

    TrustStrategy acceptingTrustStrategy = (cert, authType) -> true;
    SSLSocketFactory sf = new SSLSocketFactory(acceptingTrustStrategy, ALLOW_ALL_HOSTNAME_VERIFIER);
    httpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", 8443, sf));

    ResponseEntity response = new RestTemplate(requestFactory).
      exchange(urlOverHttps, HttpMethod.GET, null, String.class);
    assertThat(response.getStatusCode().value(), equalTo(200));
}

Mit den neuenTrustStrategy jetztoverriding the standard certificate verification process (die einen konfigurierten Vertrauensmanager konsultieren sollten) - der Test besteht jetzt undthe client is able to consume the HTTPS URL.

4. SSL konfigurieren - Alle akzeptieren (HttpClient 4.4 und höher)

Mit dem neuen HTTPClient haben wir jetzt einen erweiterten, überarbeiteten Standard-SSL-Hostnamen-Verifizierer. Mit der Einführung vonSSLConnectionSocketFactory undRegistryBuilder ist es auch einfach, SSLSocketFactory zu erstellen. So können wir den obigen Testfall wie folgt schreiben:

@Test
public final void givenAcceptingAllCertificates_whenHttpsUrlIsConsumed_thenOk()
  throws GeneralSecurityException {
    TrustStrategy acceptingTrustStrategy = (cert, authType) -> true;
    SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
    SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext,
      NoopHostnameVerifier.INSTANCE);

    Registry socketFactoryRegistry =
      RegistryBuilder. create()
      .register("https", sslsf)
      .register("http", new PlainConnectionSocketFactory())
      .build();

    BasicHttpClientConnectionManager connectionManager =
      new BasicHttpClientConnectionManager(socketFactoryRegistry);
    CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf)
      .setConnectionManager(connectionManager).build();

    HttpComponentsClientHttpRequestFactory requestFactory =
      new HttpComponentsClientHttpRequestFactory(httpClient);
    ResponseEntity response = new RestTemplate(requestFactory)
      .exchange(urlOverHttps, HttpMethod.GET, null, String.class);
    assertThat(response.getStatusCode().value(), equalTo(200));
}

5. Der SpringRestTemplate mit SSL (HttpClient <4.3)

Nachdem wir nun gesehen haben, wie ein rohesHttpClient mit SSL-Unterstützung konfiguriert wird, werfen wir einen Blick auf einen übergeordneten Client - das SpringRestTemplate.

Wenn kein SSL konfiguriert ist, schlägt der folgende Test erwartungsgemäß fehl:

@Test(expected = ResourceAccessException.class)
public void whenHttpsUrlIsConsumed_thenException() {
    String urlOverHttps
      = "https://localhost:8443/httpclient-simple/api/bars/1";
    ResponseEntity response
      = new RestTemplate().exchange(urlOverHttps, HttpMethod.GET, null, String.class);
    assertThat(response.getStatusCode().value(), equalTo(200));
}

Konfigurieren wir also SSL:

@Test
public void givenAcceptingAllCertificates_whenHttpsUrlIsConsumed_thenException()
  throws GeneralSecurityException {
    HttpComponentsClientHttpRequestFactory requestFactory
      = new HttpComponentsClientHttpRequestFactory();
    DefaultHttpClient httpClient
      = (DefaultHttpClient) requestFactory.getHttpClient();
    TrustStrategy acceptingTrustStrategy = (cert, authType) -> true
    SSLSocketFactory sf = new SSLSocketFactory(
      acceptingTrustStrategy, ALLOW_ALL_HOSTNAME_VERIFIER);
    httpClient.getConnectionManager().getSchemeRegistry()
      .register(new Scheme("https", 8443, sf));

    String urlOverHttps
      = "https://localhost:8443/httpclient-simple/api/bars/1";
    ResponseEntity response = new RestTemplate(requestFactory).
      exchange(urlOverHttps, HttpMethod.GET, null, String.class);
    assertThat(response.getStatusCode().value(), equalTo(200));
}

Wie Sie sehen können, ist diesvery similar to the way we configured SSL for the raw HttpClient. Wir konfigurieren die Anforderungsfactory mit SSL-Unterstützung und instanziieren dann die Vorlage, die diese vorkonfigurierte Factory übergibt.

6. Die FederRestTemplate mit SSL (HttpClient 4.4)

Und wir können unsereRestTemplate auf dieselbe Weise konfigurieren:

@Test
public void givenAcceptingAllCertificatesUsing4_4_whenUsingRestTemplate_thenCorrect()
throws ClientProtocolException, IOException {
    CloseableHttpClient httpClient
      = HttpClients.custom()
        .setSSLHostnameVerifier(new NoopHostnameVerifier())
        .build();
    HttpComponentsClientHttpRequestFactory requestFactory
      = new HttpComponentsClientHttpRequestFactory();
    requestFactory.setHttpClient(httpClient);

    ResponseEntity response
      = new RestTemplate(requestFactory).exchange(
      urlOverHttps, HttpMethod.GET, null, String.class);
    assertThat(response.getStatusCode().value(), equalTo(200));
}

7. Fazit

In diesem Lernprogramm wurde erläutert, wie Sie SSL für einen Apache HttpClient konfigurieren, damit dieser unabhängig vom Zertifikat eine beliebige HTTPS-URL verwenden kann. Die gleiche Konfiguration für die FederRestTemplate ist ebenfalls dargestellt.

Es ist jedoch wichtig zu verstehen, dassthis strategy entirely ignores certificate checking - was es unsicher macht und nur dort verwendet wird, wo dies sinnvoll ist.

Die Implementierung dieser Beispiele finden Sie inthe GitHub project - dies ist ein Eclipse-basiertes Projekt, daher sollte es einfach zu importieren und auszuführen sein.