Create self signed HttpClient and RestTemplate for testing

Upasana | December 05, 2019 | 8 min read | 3,320 views


If you are just looking to ignore all kind SSL certificates while RestTemplate, then you can follow this article instead - Disable SSL certificate validation in RestTemplate

Transport Layer Security (TLS) and its predecessor, Secure Sockets Layer (SSL), are technologies which allow web browsers and web servers to communicate over a secured connection. This means that the data being sent is encrypted by one side, transmitted, then decrypted by the other side before processing. This is a two-way process, meaning that both the server AND the browser encrypt all traffic before sending out data.

Another important aspect of the SSL/TLS protocol is Authentication. This means that during your initial attempt to communicate with a web server over a secure connection, that server will present your web browser with a set of credentials, in the form of a "Certificate", as proof the site is who and what it claims to be. In certain cases, the server may also request a Certificate from your web browser, asking for proof that you are who you claim to be. This is known as "Client Authentication," although in practice this is used more for business-to-business (B2B) transactions than with individual users. Most SSL-enabled web servers do not request Client Authentication.

More information can be found here [Configure SSL in Tomcat](https://tomcat.apache.org/tomcat-8.0-doc/ssl-howto.html)

Step 1. Create a Keystore for SSL

Java provides a tool named keytool in %JAVA_HOME%\bin directory. You can run genkey (or genkeypair, both are same) command, as shown in below code snippet.

C:\Users\munish>keytool -genkey -alias shunyafoundation -keyalg RSA -keysize 2048 -validity 700 -keypass changeit -storepass changeit -keystore keystore.jks
What is your first and last name?
  [Unknown]:  www.shunyafoundation.com
What is the name of your organizational unit?
  [Unknown]:  IT
What is the name of your organization?
  [Unknown]:  Shunya Foundation
What is the name of your City or Locality?
  [Unknown]:  Mohali
What is the name of your State or Province?
  [Unknown]:  Punjab
What is the two-letter country code for this unit?
  [Unknown]:  IN
Is CN=www.shunyafoundation.com, OU=Ecommerce, O=Shunya Foundation, L=Mohali, ST=Punjab, C=IN correct?
  [no]:  yes

Please note that you must supply hostname when keytool prompts for "First and Last name". This can be localhost for development/testing, if you do not provide hostname while creating keystore, SSL validation will fail complaining hostname does not match domain name in the certificate.

List and Verify Certificate Entry We can use the same keytool with list command to list the certificate details present in a given keystore.

C:\Users\munish>keytool -list -keystore keystore.jks
Enter keystore password:

Keystore type: JKS
Keystore provider: SUN

Your keystore contains 2 entries

shunyafoundation, Mar 3, 2016, PrivateKeyEntry,
Certificate fingerprint (SHA1): 60:05:46:8F:08:6B:03:7A:34:96:38:9A:6B:09:32:03:D8:FB:D6:AA
localhost, Mar 3, 2016, PrivateKeyEntry,
Certificate fingerprint (SHA1): 2F:AC:C4:94:FA:2B:4A:A7:B1:51:D8:6A:70:D6:F7:CE:92:91:67:DE

Converting to PKCS12 format

It is recommended to use the PKCS12 format which is an industry standard. We will convert our JKS keystore to PKCS12 format using the following command:

$ keytool -importkeystore -srckeystore shunya.jks -destkeystore shunya.p12 -deststoretype pkcs12

Step 2. Configure Tomcat to use Self Signed Certificate

We need to enable SSL connector in Tomcat server configuration and provide path for newly created Keystore along with keystore password, as shown below

File Path - $CATALINA_HOME/conf/server.xml

<!-- Define a SSL HTTP/1.1 Connector on port 8443
         This connector uses the JSSE configuration, when using APR, the
         connector should be using the OpenSSL style configuration
         described in the APR documentation -->

 <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
               maxThreads="150" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS"
	       keystoreFile="keystore.jks"
	       keystorePass="changeit" />

Spring Boot approach

If we are using Spring Boot, then we can configure SSL using application.yml easily:

application.yml
# The format used for the keystore. It could be set to JKS in case it is a JKS file
server.ssl.key-store-type: PKCS12
# The path to the keystore containing the certificate
server.ssl.key-store: classpath:keystore/shunya.p12
# The password used to generate the certificate
server.ssl.key-store-password: changeit
# The alias mapped to the certificate
server.ssl.key-alias: shunya

Step 3. Import Self Signed Certificate into Java (security/lib)

First of all export public certificate (that is without private key) into a .cer file that can be distributed a public clients, below is the step to create a .cer file

D:\prod-backup>keytool -exportcert -keystore shunya_keystore.jks -alias shunya -file shunya.cer
Enter keystore password:
Certificate stored in file <shunya.cer>

Now copy this shunya.cer file to %Java_Home%\jre\lib\security folder, then run the below command to import above certificate to cacerts file

C:\Program Files\Java\jdk1.8.0_74\jre\lib\security>keytool -importcert -keystore cacerts -alias shunya -file shunya.cer
Enter keystore password:
Owner: CN=www.shunyafoundation.com, OU=Shunya, O=Shunya Foundation, L=Mohali, ST=Punjab, C=IN
Issuer: CN=www.shunyafoundation.com, OU=Shunya, O=Shunya Foundation, L=Mohali, ST=Punjab, C=IN
Serial number: 39172677
Valid from: Wed Mar 02 20:24:30 IST 2016 until: Tue May 31 20:24:30 IST 2016
Certificate fingerprints:
         MD5:  49:51:FA:1A:6B:9B:78:A3:9A:A1:4C:22:88:F2:5C:87
         SHA1: B3:A8:26:82:DE:0B:BE:CA:A3:F1:D0:2D:00:A6:0A:64:EE:CB:5C:34
         SHA256: 01:EC:6C:36:46:55:03:3A:71:EF:89:4F:C3:52:34:B2:39:99:41:D2:D6:5D:9D:EF:01:5F:4A:4D:12:40:5A:E3
         Signature algorithm name: SHA256withRSA
         Version: 3
Extensions:
1: ObjectId: 2.5.29.14 Criticality=false
Trust this certificate? [no]:  yes
Certificate was added to keystore

You may require elevated administrative privilege to run the above command. Now you are all set for making Https Calls using Java programs that run on the same JVM where we imported our shunya.cer file. Below is an example of the same.

public final static void main(String[] args) throws Exception {
        CloseableHttpClient httpclient = HttpClients.createDefault();
        try {
            HttpGet httpget = new HttpGet("<https url>");
            System.out.println("Executing request " + httpget.getRequestLine());
            CloseableHttpResponse response = httpclient.execute(httpget);
            try {
                HttpEntity entity = response.getEntity();
                System.out.println(response.getStatusLine());
                EntityUtils.consume(entity);
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }
    }

Step 4. Creating a HttpClient that loads custom keystore for self signed SSL certificate

Also, you may create a another keystore that contains this public certificate, without any private key in it. This keystore can be freely distributed to public for SSL Communication

D:\prod-backup>keytool -importcert -file shunya.cer -keystore shunya.jks -alias shunya
Enter keystore password:
Re-enter new password:
Owner: CN=www.shunyafoundation.com, OU=Shunya, O=Shunya Foundation, L=Mohali, ST=Punjab, C=IN
Issuer: CN=www.shunyafoundation.com, OU=Shunya, O=Shunya Foundation, L=Mohali, ST=Punjab, C=IN
Serial number: 39172677
Valid from: Wed Mar 02 20:24:30 IST 2016 until: Tue May 31 20:24:30 IST 2016
Certificate fingerprints:
         MD5:  49:51:FA:1A:6B:9B:78:A3:9A:A1:4C:22:88:F2:5C:87
         SHA1: B3:A8:26:82:DE:0B:BE:CA:A3:F1:D0:2D:00:A6:0A:64:EE:CB:5C:34
         SHA256: 01:EC:6C:36:46:55:03:3A:71:EF:89:4F:C3:52:34:B2:39:99:41:D2:D6:5D:9D:EF:01:5F:4A:4D:12:40:5A:E3
         Signature algorithm name: SHA256withRSA
         Version: 3

Extensions:

#1: ObjectId: 2.5.29.14 Criticality=false

Trust this certificate? [no]:  yes
Certificate was added to keystore

Now this new shunya.jks keystore can be used by httpClient for SSL communication. The code below explains the use of shunya.jks keystore by HttpClient

import java.io.File;

import javax.net.ssl.SSLContext;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;

public class ClientCustomSSL {
    public final static void main(String[] args) throws Exception {
        // Trust own CA and all self-signed certs
        SSLContext sslcontext = SSLContexts.custom()
                .loadTrustMaterial(new File("shunya.jks"), "changeit".toCharArray(), new TrustSelfSignedStrategy())
                .build();
        // Allow TLSv1 protocol only
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,
                SSLConnectionSocketFactory.getDefaultHostnameVerifier());
        CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
        try {
            HttpGet httpget = new HttpGet("<Https Url>");
            System.out.println("Executing request " + httpget.getRequestLine());
            CloseableHttpResponse response = httpclient.execute(httpget);
            try {
                HttpEntity entity = response.getEntity();
                System.out.println(response.getStatusLine());
                EntityUtils.consume(entity);
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }
    }
}

Step 5. Creating RestTemplate using HttpClient that accepts custom Self Signed Certificate (in Memory loading)

RestTemplate uses HttpClient under the hood, so we can easily configure RestTemplate to use custom self-signed certificate by loading the keystore and creating the sslContext using this keystore.

RestTemplate using custom self-seigned certificate
        //Load the keystore into stream inside a jar (in memory keystore loading)
        InputStream keyStoreInputStream = RemoteService.class.getClass().getClassLoader().getResourceAsStream("keystore.jks");
        if (keyStoreInputStream == null) {
            throw new FileNotFoundException("Could not find file named 'shunya_keystore' in the CLASSPATH");
        }
        final KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        try {
            trustStore.load(keyStoreInputStream, "shunya".toCharArray());
        } finally {
            keyStoreInputStream.close();
        }

        SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()).build();
        // Allow TLSv1 protocol only
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier());
        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();

        HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
        httpComponentsClientHttpRequestFactory.setConnectTimeout(60000);
        httpComponentsClientHttpRequestFactory.setReadTimeout(180000);

        final RestTemplate restTemplate = new RestTemplate(httpComponentsClientHttpRequestFactory);

        ResponseEntity<String> response = restTemplate.exchange(urlOverHttps, HttpMethod.GET, null, String.class);

         assertThat(response.getStatusCode().value(), equalTo(200));

Step 6. HttpClient that ignores SSL certificate altogether (not a proper way, just for demonstration)

This approach is not recommended for production use.

HttpClient provides option to ignore all the SSL certificates and proceed without doing any validation using NoopHostnameVerifier, as shown in the code below:

ClientIgnoreSSLCertificate.java
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
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.SSLContexts;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;

public class ClientIgnoreSSLCertificate {

    public final static void main(String[] args) throws Exception {
        // Ignore All SSL certificate
        SSLContext sslcontext = SSLContexts.custom()
                .loadTrustMaterial(null, (chain, authType) -> true)
                .build();
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, new NoopHostnameVerifier());
        CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
        try {
            HttpGet httpget = new HttpGet("<Https Url>");
            System.out.println("Executing request " + httpget.getRequestLine());
            CloseableHttpResponse response = httpclient.execute(httpget);
            try {
                HttpEntity entity = response.getEntity();
                System.out.println(response.getStatusLine());
                EntityUtils.consume(entity);
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }
    }
}

Step 7. Using HttpsUrlConnection with Custom SSL certificate

We can use HttpsUrlConnection with custom SSL certificate in pretty much similar way. We will load custom keystore to initialize the SSL context.

public final static void main(String[] args) throws Exception {
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        final FileInputStream trustStore = new FileInputStream(new File("shunya_keystore.jks"));
        keyStore.load(trustStore, "<password>".toCharArray());
        trustStore.close();

        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(keyStore);
        SSLContext ctx = SSLContext.getInstance("TLS");
        //Older versions of SSL may use it like this
//      SSLContext ctx = SSLContext.getInstance("SSL");
        ctx.init(null, tmf.getTrustManagers(), null);
        SSLSocketFactory sslFactory = ctx.getSocketFactory();

        URL url = new URL("<Https Url>");
        HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
        conn.setSSLSocketFactory(sslFactory);
        conn.setRequestMethod("GET");
        conn.setDoOutput(true);
        InputStreamReader isr = new InputStreamReader(conn.getInputStream());

        // read it with BufferedReader
        BufferedReader br = new BufferedReader(isr);
        String line;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
        br.close();
    }

Just ignoring all SSL certificates in RestTemplate

References


Buy my ebook for complete question bank

Most of these questions has been answered in my eBook "Cracking the Core Java Interview" updated on June 2018, that you can buy from this link:

Buy from Shunya (DRM Free PDF download with updates)

Top articles in this category:
  1. REST Assured vs Apache HttpClient and RestTemplate
  2. 50 Java Interview Questions for SDET Automation Engineer
  3. Rest Assured API Testing Interview Questions
  4. Junit interview questions for SDET automation engineer
  5. OAuth2 protected resources using RestTemplate
  6. Java 11 HttpClient with Basic Authentication
  7. HTTP Head request using Java 11 HttpClient - Kotlin

Recommended books for interview preparation:

Find more on this topic: