HttpClient avec SSL

HttpClient avec SSL

1. Vue d'ensemble

Cet article montrera commentconfigure the Apache HttpClient 4 with “Accept All” SSL support. L'objectif est simple: consommez des URL HTTPS sans certificat valide.

Si vous voulez approfondir et apprendre d'autres choses intéressantes que vous pouvez faire avec HttpClient, rendez-vous surthe main HttpClient guide.

Lectures complémentaires:

Gestion de la connexion HttpClient

Comment ouvrir, gérer et fermer des connexions avec Apache HttpClient 4.

Read more

Configuration avancée du client

Configurations HttpClient pour les cas d'utilisation avancés.

Read more

Comment envoyer des cookies personnalisés avec Apache HttpClient 4.

Read more

2. LesSSLPeerUnverifiedException

Sans configurer SSL avec lesHttpClient, le test suivant - consommant une URL HTTPS - échouera:

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));
    }
}

L'échec exact est:

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)
    ...

Lejavax.net.ssl.SSLPeerUnverifiedException exception se produit chaque fois qu'une chaîne de confiance valide n'a pas pu être établie pour l'URL.

3. Configurer SSL - Tout accepter (HttpClient <4.3)

Configurons maintenant le client HTTP pour qu'il approuve toutes les chaînes de certificats, quelle que soit leur validité:

@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));
}

Avec le nouveauTrustStrategy maintenantoverriding the standard certificate verification process (qui devrait consulter un gestionnaire de confiance configuré) - le test réussit maintenant etthe client is able to consume the HTTPS URL.

4. Configurer SSL - Tout accepter (HttpClient 4.4 et supérieur)

Avec le nouveau HTTPClient, nous disposons maintenant d’un vérificateur de nom d’hôte SSL amélioré, redessiné. De plus, avec l’introduction deSSLConnectionSocketFactory etRegistryBuilder, il est facile de créer SSLSocketFactory. Donc, nous pouvons écrire le cas de test ci-dessus comme:

@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. Le SpringRestTemplate avec SSL (HttpClient <4.3)

Maintenant que nous avons vu comment configurer unHttpClient brut avec le support SSL, jetons un œil à un client de niveau supérieur - le SpringRestTemplate.

En l'absence de SSL configuré, le test suivant échoue comme prévu:

@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));
}

Alors configurons 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));
}

Comme vous pouvez le voir, il s'agit devery similar to the way we configured SSL for the raw HttpClient - nous configurons la fabrique de requêtes avec le support SSL, puis nous instancions le modèle en passant cette fabrique préconfigurée.

6. Le SpringRestTemplate avec SSL (HttpClient 4.4)

Et nous pouvons utiliser la même manière pour configurer nosRestTemplate:

@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. Conclusion

Ce tutoriel explique comment configurer SSL pour un serveur Apache HttpClient afin qu'il puisse utiliser n'importe quelle URL HTTPS, quel que soit le certificat. La même configuration pour les SpringRestTemplate est également illustrée.

Une chose importante à comprendre cependant est quethis strategy entirely ignores certificate checking - ce qui le rend non sécurisé et ne doit être utilisé que lorsque cela a du sens.

L'implémentation de ces exemples peut être trouvée dansthe GitHub project - il s'agit d'un projet basé sur Eclipse, il devrait donc être facile à importer et à exécuter tel quel.