HttpClient com SSL

HttpClient com SSL

1. Visão geral

Este artigo mostrará comoconfigure the Apache HttpClient 4 with “Accept All” SSL support. O objetivo é simples - consumir URLs HTTPS que não possuem certificados válidos.

Se você quiser se aprofundar e aprender outras coisas legais que você pode fazer com o HttpClient - vá parathe main HttpClient guide.

Leitura adicional:

Gerenciamento de conexões HttpClient

Como abrir, gerenciar e fechar conexões com o Apache HttpClient 4.

Read more

Configuração avançada do HttpClient

Configurações HttpClient para casos de uso avançados.

Read more

Como enviar cookies personalizados com o Apache HttpClient 4.

Read more

2. OSSLPeerUnverifiedException

Sem configurar SSL comHttpClient, o seguinte teste - consumindo um URL HTTPS - falhará:

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

A falha exata é:

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

Ojavax.net.ssl.SSLPeerUnverifiedException exception ocorre sempre que uma cadeia de confiança válida não pode ser estabelecida para o URL.

3. Configurar SSL - Aceitar tudo (HttpClient <4.3)

Vamos agora configurar o cliente HTTP para confiar em todas as cadeias de certificados, independentemente de sua validade:

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

Com o novoTrustStrategy agoraoverriding the standard certificate verification process (que deve consultar um gerenciador de confiança configurado) - o teste agora passa ethe client is able to consume the HTTPS URL.

4. Configurar SSL - Aceitar tudo (HttpClient 4.4 e superior)

Com o novo HTTPClient, agora temos um verificador de nome de host SSL aprimorado e redesenhado. Além disso, com a introdução deSSLConnectionSocketFactory eRegistryBuilder, é fácil construir SSLSocketFactory. Então, podemos escrever o caso de teste acima, como:

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

Agora que vimos como configurar umHttpClient bruto com suporte a SSL, vamos dar uma olhada em um cliente de nível superior - o SpringRestTemplate.

Sem SSL configurado, o seguinte teste falha conforme o esperado:

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

Então, vamos configurar o 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));
}

Como você pode ver, évery similar to the way we configured SSL for the raw HttpClient - configuramos a fábrica de solicitação com suporte SSL e, em seguida, instanciamos o modelo passando por essa fábrica pré-configurada.

6. SpringRestTemplate com SSL (HttpClient 4.4)

E podemos usar a mesma maneira para configurar nossoRestTemplate:

@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. Conclusão

Este tutorial discutiu como configurar o SSL para um Apache HttpClient para que ele consuma qualquer URL HTTPS, independentemente do certificado. A mesma configuração para SpringRestTemplate também é ilustrada.

Uma coisa importante a entender, entretanto, é quethis strategy entirely ignores certificate checking - o que o torna inseguro e só deve ser usado onde fizer sentido.

A implementação desses exemplos pode ser encontrada emthe GitHub project - este é um projeto baseado em Eclipse, portanto, deve ser fácil de importar e executar como está.