How to call a REST API protected with SSL (https) from Spring Boot without importing the certificate into java keystore ?

In the previous post we saw how to consume a REST API protected with SSL (HTTPS) by importing necessary SSL certificates into JVM keystore

That serves fine if you have access to the JVM . In case if you don’t and want to bundle those certificates along with your application and use it to call the protected REST API you can follow the below algorithm:

STEP1: Get the certificates

STEP2: Create a keystore using those certificates

STEP3: Place the keystore in your application classpath (resources folder)

STEP4: Create a custom REST Template which will fetch your keystore

STEP5: Call the protected REST API using the custom REST Template

Here are the details:

STEP1: Get the certificates

As explained in the previous post :

you can download the certificates from any popular browser.

STEP2: Create a keystore

You can do this using a tool like KeyStoreExplorer :

https://keystore-explorer.org/

It is intuitive and simple to use. Just create a keystore with the certificates you downloaded.

Here I am creating my own certificate and then adding it to a new keystore.

You can import the certificates you downloaded directly into a new keystore instead ,as well.

Click on Generate Key Pair:

Click on the icon near Name

Click on Add Extensions. You need to do this to add your domain and ip address in case if you are testing from your local machine

Choose extension type as Subject Alternative Name:

Under that add DNS and IP address:

Give an alias name and password:

Save the keystore with .p12 extension after giving a password for the keystore just like you gave for keypair:

The keystore is now ready with the certificate I created!

As already mentioned you can import any certificate directly into the keystore instead of creating a new key pair.

Before consuming a secured REST API , let’s see how to create a secured REST API first using the above keystore.

To do this , just place the keystore you created using the above steps in the resources folder of the application you want to protect:

This is a sample spring boot application created with a simple REST API:

package com.example.demo;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

	
	
	@GetMapping("/hello")
	public String sayHello() {
		
		return "hello";
	}
}

We need to protect the above API (/hello) with the SSL certificate we just created.

To do this just use the below properties in application yml file:

server:
   ssl:
     key-store: classpath:mydemocertificate.p12
     key-store-password: mydemo
     key-alias: mydemocertificate
   port: 8443

The API is now protected with SSL and runs on port 8443 as most https applications run so.

Now when you try to access this API in the browser you get this:

This is because the browser doesn’t know the certificate you created . You need to click on Advanced button and ask the browser to trust this certificate. It will then add it to the list of trusted certificates:

I got the output now:

Now let’s move to the next step to understand how to consume the above API from another spring boot application using REST Template

STEP3: Place the keystore in resources folder:

Just the same way you placed the keystore in resources folder for the application you wanted to secure , place the same keystore in the application from which you want to consume the protected application.

STEP4: Create a custom REST Template

If you use a blank rest template and try to consume the secured API , you will get “unable to find certification path …” issue.

For example , if you use the below code:

package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class TestController {

	
	
	@GetMapping("/message")
	public String fetchMessage() {

		RestTemplate restTemplate = new RestTemplate();
		String response = restTemplate.getForObject("https:localhost:8443/hello", String.class);

		return response;
	}
}

and hit the API /hello ( the above spring boot application runs on port 8080 and doesn’t use https) , you get the below error message in browser:

In the console you see the below stack trace:

To resolve this use a custom rest template instead of a blank one.

Here is a custom rest template I created which loads the keystore which we placed in the resources folder:

package com.example.demo;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;

import javax.net.ssl.SSLContext;

import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

@Configuration
public class CustomConfiguration {

	@Bean
	public RestTemplate restTemplate() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException,
			CertificateException, MalformedURLException, IOException {

		SSLContext sslContext = new SSLContextBuilder()
				.loadTrustMaterial(new URL("file:src/main/resources/mydemocertificate.p12"), "mydemo".toCharArray()).build();
		SSLConnectionSocketFactory sslConFactory = new SSLConnectionSocketFactory(sslContext);

		CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslConFactory).build();
		ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
		RestTemplate restTemplate = new RestTemplate(requestFactory);

		return restTemplate;

	}
}

We are setting the SSL context here with the new keystore which we created using the KeyStoreExplorer tool. We are creating a REST Template Spring Bean by annotating the above method with @Bean and the entire class with @Configuration .

STEP5: Call protected API using custom REST Template

Now replace the Rest Controller code with below which autowires the above REST template

package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class TestController {

	@Autowired
	private RestTemplate restTemplate;

	@GetMapping("/message")
	public String fetchMessage() {

		String response = restTemplate.getForObject("https://localhost:8443/hello", String.class);

		return response;
	}
}

Hit http://localhost:8080/message now and see the response!

That’s it!

Here is the application protected with SSL :

https://github.com/vijaysrj/ssldemo

Here is the application which consumes a REST API of the above:

https://github.com/vijaysrj/sslclient


Posted

in

,

by

Comments

Leave a Reply

Discover more from The Full Stack Developer

Subscribe now to keep reading and get access to the full archive.

Continue reading