In Spring RestTemplate, when connecting to an HTTPS endpoint with a self-signed certificate, we would need to configure the TrustStore to get the response properly

Self-signed certificates are not issued by known Certificate Authorities (CA) but rather by the server hosting the certificate

TrustStore in Java is used to store certificates of thrid parties

The following example sends a GET request to self-signed.badssl.com with a normal RestTemplate

restTemplate.getForEntity("https://self-signed.badssl.com/", String.class, Collections.emptyMap());

Then the SSLHandshakeException response is expected output

javax.net.ssl.SSLHandshakeException: PKIX path building failed  
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

This tutorial walks you through the steps of connecting to a self-signed cert URL in Spring RestTemplate. We use self-signed.badssl.com as the example server endpoint

Get the self-signed cert

You may download the self-signed cert by using either your browser or the openssl command-line tool

The below is using openssl to download the cert and output to the cert file badssl-com.pem

echo quit\  
 | openssl s_client -servername self-signed.badssl.com -showcerts -connect self-signed.badssl.com:443\
 | openssl x509 -outform PEM\
 > badssl-com.pem

Convert pem to p12 file

Java supports 2 file formats jks and p12 (default since Java 9) for storing keys and certificates

You may use keytool to convert the pem file to p12 file. keytool is a command-line utility shipped by default with JRE/JDK

keytool -import -keystore badssl-com.p12 -alias badssl-com -file badssl-com.pem -trustcacerts

keytool will ask you to enter the password for badssl-com.p12 and if you trust the cert

Enter keystore password: changeit  
Re-enter new password: changeit  
Trust this certificate? [no]: yes  

Configure TrustStore

Copy the badssl-com.p12 file to resources folder

├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── example
│   │   │           └── demo
│   │   │               └── DemoApplication.java
│   │   ├── resources
│   │   │   ├── application.properties
│   │   │   ├── badssl-com.p12

Spring RestTemplate is a wrapper of multiple HTTP client instances such as the default URLConnection or Apache HTTPClient. In this example, we configure the TrustStore with Apache HttpClient, the dependency can be included in the pom.xml as below

<dependency>  
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</dependency>

We use loadTrustMaterial(URL url, char[] storePassword) method of SSLContextBuilder class to configure the trust store in RestTemplate

@Bean
public RestTemplate restTemplateWithTrustStore(RestTemplateBuilder builder) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {  
    SSLContext sslContext = new SSLContextBuilder()
        .loadTrustMaterial(trustStore.getURL(), trustStorePassword.toCharArray())
        .build();
    SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext);

    HttpClient httpClient = HttpClients.custom()
        .setSSLSocketFactory(socketFactory)
        .build();

    return builder
        .requestFactory(() -> new HttpComponentsClientHttpRequestFactory(httpClient))
        .build();
}

The trustStore and trustStorePassword are settings loaded from application.properties and @Value binding

trust-store=classpath:badssl-com.p12  
trust-store-password=changeit
@Value("${trust-store}")
private Resource trustStore;

@Value("${trust-store-password}")
private String trustStorePassword;

Testing

Now we execute the GET request again with the above restTemplateWithTrustStore. The response status should be 200

@Autowired private RestTemplate restTemplateWithTrustStore;

@Test
public void okResponse() throws Exception {  
    ResponseEntity<String> response = restTemplateWithTrustStore
        .getForEntity("https://self-signed.badssl.com/", String.class, Collections.emptyMap());

    assertEquals(HttpStatus.OK, response.getStatusCode());
}

Conclusion

In this tutorial, we learned how to get the self-signed cert, import it into TrustStore and configure Spring RestTemplate to execute HTTPS requests to self-signed APIs