HttpClient with SSL

1. Overview

This article will show how to configure the Apache HttpClient 4 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:

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

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

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

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

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.4 and Above)

With the new HTTPClient, now we have an enhanced, redesigned default SSL hostname verifier. Also with the introduction of SSLConnectionSocketFactory and RegistryBuilder, it’s easy to build SSLSocketFactory. So we can write the above test case like :

@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 < 4.3)

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

So let’s configure 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<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.4)

And we can use the same way to configure our RestTemplate:

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

Related posts:

Validate email address exists or not by Java Code
Optional trong Java 8
Java Program to Implement LinkedBlockingDeque API
Java Program to Perform Sorting Using B-Tree
Guide to Dynamic Tests in Junit 5
Java Program to Find Hamiltonian Cycle in an UnWeighted Graph
Hướng dẫn Java Design Pattern – Transfer Object
Làm thế nào tạo instance của một class mà không gọi từ khóa new?
How to Get All Dates Between Two Dates?
Java Program to Perform Uniform Binary Search
Hướng dẫn Java Design Pattern – Template Method
Java Program to Implement Sorted Circularly Singly Linked List
Java Program to Implement Quick Hull Algorithm to Find Convex Hull
Thao tác với tập tin và thư mục trong Java
Pagination and Sorting using Spring Data JPA
Java Program to Use the Bellman-Ford Algorithm to Find the Shortest Path
Java Program to Perform Naive String Matching
Java Program to Implement Sorted Singly Linked List
Getting Started with GraphQL and Spring Boot
Giới thiệu Google Guice – Dependency injection (DI) framework
SOAP Web service: Upload và Download file sử dụng MTOM trong JAX-WS
Java Program to Perform the Shaker Sort
Collect a Java Stream to an Immutable Collection
A Guide to Java HashMap
Lập trình đa luồng trong Java (Java Multi-threading)
Hướng dẫn Java Design Pattern – Dependency Injection
Java Program to Implement Knight’s Tour Problem
Map to String Conversion in Java
Limiting Query Results with JPA and Spring Data JPA
Java Program to Implement Booth Algorithm
Hướng dẫn sử dụng luồng vào ra ký tự trong Java
Encode/Decode to/from Base64