Using RestTemplate with client certificates

How to use RestTemplate for making an HTTP call with certificates and keys in a Spring Boot application.

Isurie K. Liyanage
Tech x Talent
4 min readApr 2, 2024

--

Secure communication over HTTPS is vital in modern web applications to protect sensitive data during transit. When integrating with external services or APIs that require client certificate authentication, configuring RestTemplate in Spring becomes essential. This article will explore how to set up RestTemplate to communicate securely using client certificates.

Configuring RestTemplate with Client Certificates: To set up RestTemplate for communicating over HTTPS with client certificates, follow these steps:

  1. Create a Spring Configuration Class:

Configure RestTemplate with Client Certificates To set up RestTemplate for secure communication using client certificates, create a configuration class as follows:

// RestTemplate configuration
@Configuration
public class RestClientConfig {

@Value("${ssl.client.keystore.path}")
private String keyStoreResourcePath;

@Value("${ssl.client.truststore.path}")
private String trustStoreResourcePath;

@Value("${ssl.client.keystore.password}")
private String keyStorePassword;

@Autowired
public purchaseService(@Qualifier("purchaseRestTemplate") RestTemplate restClientConfig) {
this.purchaseRestTemplate = restClientConfig;
}

@Bean
public RestTemplate restTemplate() {
SSLContext sslContext = null;
try {
sslContext = SSLContext.getInstance("TLS");

// Load client certificate and private key
KeyStore keyStore = KeyStore.getInstance("PKCS12");
char[] keyStorePasswordArray = keyStorePassword.toCharArray();
Resource keyStoreResource = new FileSystemResource(keyStoreResourcePath);
URL keyStoreUrl = keyStoreResource.getURL();
if (keyStoreUrl == null) {
throw new FileNotFoundException("Keystore file not found on classpath");
}
keyStore.load(keyStoreUrl.openStream(), keyStorePasswordArray);

KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, keyStorePasswordArray);

// Load trust store
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
Resource trustStoreResource = new FileSystemResource(trustStoreResourcePath);
URL trustStoreUrl = trustStoreResource.getURL();
if (trustStoreUrl == null) {
throw new FileNotFoundException("Truststore file not found on classpath");
}
trustStore.load(trustStoreUrl.openStream(), keyStorePasswordArray);

// Initialize SSL context
sslContext.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom());

return new RestTemplate(new CustomRequestFactory(sslContext));
} catch (NoSuchAlgorithmException | UnrecoverableKeyException | CertificateException | KeyStoreException |
IOException | KeyManagementException e) {
throw new RuntimeException(e);
}
}

// Other configurations...
private static class CustomRequestFactory extends org.springframework.http.client.SimpleClientHttpRequestFactory {

private final SSLContext sslContext;

public CustomRequestFactory(SSLContext sslContext) {
this.sslContext = sslContext;
}

@Override
protected void prepareConnection(java.net.HttpURLConnection connection, String httpMethod) throws IOException {
if (connection instanceof javax.net.ssl.HttpsURLConnection) {
((javax.net.ssl.HttpsURLConnection) connection).setSSLSocketFactory(sslContext.getSocketFactory());
//((javax.net.ssl.HttpsURLConnection) connection).setHostnameVerifier((hostname, session) -> true);// In a secure production environment, hostname verification should be enabled to ensure that the server being accessed is the intended one and to prevent potential security vulnerabilities
}
super.prepareConnection(connection, httpMethod);
}
}

}

Explanation:

  • This configuration class sets up RestTemplate to communicate securely over HTTPS.
  • It loads the client certificate and private key from a PKCS12 keystore and trust store from a JKS truststore.
  • The SSL context is initialized with key managers obtained from the client keystore.
  • It returns a RestTemplate configured with a custom request factory (CustomRequestFactory) to set up SSL context and hostname verifier for HTTPS connections.

2. Use RestTemplate in Service Classes

Let’s see how to use the configured RestTemplate in-service classes. Below is an example service class that communicates with an external API using RestTemplate:

@Slf4j
@Service
public class purchaseService {

private final RestTemplate purchaseRestTemplate;

@Value("${external.purchapi.username}")
private String username;

@Value("${external.purchapi.password}")
private String password;

// Other fields and constructors...

@Autowired
public purchaseService(@Qualifier("purchaseRestTemplate") RestTemplate restClientConfig) {
this.purchaseRestTemplate = restClientConfig;
}

// Rest of the service class code...
}

Additional Steps to Use CA Certificate and Client Certificate with a Key in RestTemplate:

Install OpenSSL: Make sure OpenSSL is installed on your system. You can download and install it from the OpenSSL website or use a package manager like Homebrew (for macOS) or apt-get (for Linux).

OpenSSL — Installation under Windows

  1. Create a PKCS12 (P12) File: Use OpenSSL to create a PKCS12 file containing the client certificate and private key.

Sample Command:

openssl pkcs12 -export -out certificate.p12 -inkey testservercert.key -in cent_test.crt

3. Create a Java KeyStore (JKS) File: Create a new Java KeyStore (JKS) file and import the CA certificate into it. You may need to enter a keystore password.

Sample Command:

keytool -import -file external_ca_test.crt -alias ca-alias -keystore truststore.jks

The bellow command can be used to view the certificates stored in the truststore, which are used to establish trust relationships with external entities, such as servers

keytool -list -keystore truststore.jks

Sample Curl Command:

curl -ki --cacert '/ibl/app/bnk/assest/external_ca_test.crt' 
--cert '/ibl/app/bnk/assest/cent_test.crt'
--key '/ibl/app/bnk/assest/testservercert.key'
--location --header 'Authorization: Basic REPLACE_WITH_BASE64_ENCODED_CREDENTIALS'
--request POST 'https://REPLACE_WITH_IP:REPLACE_WITH_PORT/getinfo'
--header 'Content-Type: application/xml'
--data-raw '<?xml version="1.0" encoding="UTF-8"?><ns0:getinforequest xmlns:ns0="http://www.ericsson.com/em/emm/provisioning/v1"><identity>ID:999999999/MSISDN</identity></ns0:getinforequest>'

This curl command demonstrates how to securely make an HTTP POST request to an API endpoint using client certificates and authentication credentials. Here's a breakdown:

  • --cacert '/ibl/app/bnk/assest/external_ca_test.crt': Specifies the path to the CA (Certificate Authority) certificate file used to verify the server's certificate.
  • --cert '/ibl/app/bnk/assest/cent_test.crt': Specifies the path to the client certificate file used for client authentication.
  • --key '/ibl/app/bnk/assest/testservercert.key': Specifies the path to the private key file corresponding to the client certificate.
  • --location: Instructs curl to follow HTTP redirects if the server responds with a redirect.
  • --header 'Authorization: Basic REPLACE_WITH_BASE64_ENCODED_CREDENTIALS': Adds an Authorization header using HTTP Basic authentication. Replace this with your Base64 encoded credentials. For example, username:password encoded in Base64.
  • --request POST: Specifies that the HTTP method for the request is POST.
  • 'https://REPLACE_WITH_IP:REPLACE_WITH_PORT/getinfo': The URL of the API endpoint to which the request is made. Replace this with the IP address and port where the request should be sent.
  • --header 'Content-Type: application/xml': Adds a Content-Type header specifying that the request body is in XML format.
  • --data-raw '<?xml version="1.0" encoding="UTF-8"?><ns0:getinforequest xmlns:ns0="http://www.ericsson.com/em/emm/provisioning/v1"><identity>ID:999999999/MSISDN</identity></ns0:getinforequest>': Specifies the raw XML data to be sent in the request body, containing information about the account holder.

Remember to replace these placeholders with your actual values before using the command.

In this article, we’ve explored how to configure RestTemplate with client certificates, along with CA certificate usage to ensures robust security for communication in a Spring Boot application. By following the outlined steps, you can effectively integrate client certificate authentication into your Spring applications, enhancing the security of your communication channels when interacting with external services or APIs over HTTPS. This approach not only strengthens the security posture of your application but also ensures the confidentiality and integrity of sensitive data transmitted between systems.

--

--

Isurie K. Liyanage
Tech x Talent

Technical Writer | BSc. (Hons.) IT — UoM | Software Engineer