1. Overview

This article will show how to configure the Apache HttpClient 4 & 5 with “Accept All” SSL support. The goal is simple – consume HTTPS URLs which do not have valid certificates.

If you want to dig deeper and learn other cool things you can do with the HttpClient – head on over to the main HttpClient guide.

2. The SSLPeerUnverifiedException

Without configuring SSL with the HttpClient, the following test – consuming an HTTPS URL – will fail:

@Test
void whenHttpsUrlIsConsumed_thenException() {
    String urlOverHttps = "https://localhost:8082/httpclient-simple";
    HttpGet getMethod = new HttpGet(urlOverHttps);

    assertThrows(SSLPeerUnverifiedException.class, () -> {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpResponse response = httpClient.execute(getMethod, new CustomHttpClientResponseHandler());
        assertThat(response.getCode(), equalTo(200));
    });
}

The exact failure is:

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

The javax.net.ssl.SSLPeerUnverifiedException exception occurs whenever a valid chain of trust couldn’t be established for the URL.

3. Configure SSL – Accept All (HttpClient 5)

Let’s now configure the HTTP client to trust all certificate chains regardless of their validity:

@Test
void givenAcceptingAllCertificates_whenHttpsUrlIsConsumed_thenOk() throws GeneralSecurityException, IOException {

    final HttpGet getMethod = new HttpGet(HOST_WITH_SSL);

    final TrustStrategy acceptingTrustStrategy = (cert, authType) -> true;
    final SSLContext sslContext = SSLContexts.custom()
        .loadTrustMaterial(null, acceptingTrustStrategy)
        .build();
    final SSLConnectionSocketFactory sslsf = 
        new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
    final Registry<ConnectionSocketFactory> socketFactoryRegistry = 
        RegistryBuilder.<ConnectionSocketFactory> create()
        .register("https", sslsf)
        .register("http", new PlainConnectionSocketFactory())
        .build();

    final BasicHttpClientConnectionManager connectionManager =
        new BasicHttpClientConnectionManager(socketFactoryRegistry);

    try( CloseableHttpClient httpClient = HttpClients.custom()
        .setConnectionManager(connectionManager)
        .build();

        CloseableHttpResponse response = (CloseableHttpResponse) httpClient
            .execute(getMethod, new CustomHttpClientResponseHandler())) {

            final int statusCode = response.getCode();
            assertThat(statusCode, equalTo(HttpStatus.SC_OK));
    }
}

With the new TrustStrategy now overriding the standard certificate verification process (which should consult a configured trust manager) – the test now passes and the client is able to consume the HTTPS URL.

4. Configure SSL – Accept All (HttpClient 4.5)

@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<ConnectionSocketFactory> socketFactoryRegistry = 
      RegistryBuilder.<ConnectionSocketFactory> 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<String> response = new RestTemplate(requestFactory)
      .exchange(urlOverHttps, HttpMethod.GET, null, String.class);
    assertThat(response.getStatusCode().value(), equalTo(200));
}

5. The Spring RestTemplate with SSL (HttpClient 5)

Now that we have seen how to configure a raw HttpClient with SSL support, let’s take a look at a higher level client – the Spring RestTemplate.

With no SSL configured, the following test fails as expected:

@Test
void whenHttpsUrlIsConsumed_thenException() {
    final String urlOverHttps = "https://localhost:8443/httpclient-simple/api/bars/1";

    assertThrows(ResourceAccessException.class, () -> {
        final ResponseEntity<String> response = new RestTemplate()
            .exchange(urlOverHttps, HttpMethod.GET, null, String.class);
        assertThat(response.getStatusCode().value(), equalTo(200));
    });
}

So let’s configure SSL:

@Test
void givenAcceptingAllCertificates_whenHttpsUrlIsConsumed_thenOk() throws GeneralSecurityException {

    final TrustStrategy acceptingTrustStrategy = (cert, authType) -> true;
    final SSLContext sslContext = SSLContexts.custom()
        .loadTrustMaterial(null, acceptingTrustStrategy)
        .build();
    final SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
    final Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create()
        .register("https", sslsf)
        .register("http", new PlainConnectionSocketFactory())
        .build();

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

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

As you can see, this is very similar to the way we configured SSL for the raw HttpClient – we configure the request factory with SSL support and then we instantiate the template passing this preconfigured factory.

6. The Spring RestTemplate with SSL (HttpClient 4.5)

@Test
void givenAcceptingAllCertificates_whenUsingRestTemplate_thenCorrect() {
    final CloseableHttpClient httpClient = HttpClients.custom()
        .setSSLHostnameVerifier(new NoopHostnameVerifier())
        .build();
    final HttpComponentsClientHttpRequestFactory requestFactory
        = new HttpComponentsClientHttpRequestFactory();
    requestFactory.setHttpClient(httpClient);

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

7. Conclusion

This tutorial discussed how to configure SSL for an Apache HttpClient so that it is able to consume any HTTPS URL, regardless of the certificate. The same configuration for the Spring RestTemplate is also illustrated.

An important thing to understand however is that this strategy entirely ignores certificate checking – which makes it insecure and only to be used where that makes sense.

The implementation of these examples can be found in the GitHub project – this is an Eclipse based project, so it should be easy to import and run as it is. Apache HttpClient 4 examples can be found in our Apache HttpClient 4 module.