Authenticating and Securing Communication between Micro-Services Part-2

In the previous tutorial, we learned about implementing 1-Way-TLS, its usage, and creating a self-signed certificate. If you haven't gone through it, I would recommend going through the previous tutorial first(Authenticating and Securing Communication between Micro-Services Part-1) before going through this. In this tutorial, we will be extending the 1-Way-TLS functionality implemented in the previous tutorial to 2-Way-TLS to enhance the security between communicating micro-services.
 

Table of Content: 

  1. A brief explanation of the 2-Way-TLS handshake process with a diagram
  2. Enable 2-Way-TLS on Service-1
  3. Call Service-1's endpoint from Service2 to see the result
  4. Create Self Signed Certificate for Service2 
  5. Start sending certificate from Service2 to Service1
  6. Call Service-1's endpoint from Service2 to see the result
  7. Create and set the trustStore for Service1
  8. Result 

Github Link: 

Service1 : https://github.com/joyshah/service1 (Branch Name:feature/2-Way-TLS
  • Git clone https://github.com/joyshah/service1.git
  • Git checkout feature/2-Way-TLS
Service2 : https://github.com/joyshah/service2 (Branch Name:feature/2-Way-TLS
  • Git clone https://github.com/joyshah/service2.git
  • Git checkout feature/2-Way-TLS

1. A brief explanation of the 2-Way-TLS handshake process with a diagram

When 2-Way-SSL/TLS connection is established between a client and a server, it follows the same steps as 1-Way-TLS except one which is explained below.

Establishing a TCP/IP Connection with 2-Way-SSL/TLS Session

When the server responds to the "ClientHello" with the server certificate, it also includes the Client Certificate Request. The client verifies & authenticates the server certificate with its trust store and respond server with the client certificate. The server verifies and authenticates the client certificate with its trust store. If it's successful, it will continue with similar steps as it did in 1-Way-TLS. The difference between 1-Way-TLS and 2-Way-TLS is highlighted in red in the above image.

In precise, in 2-Way-TLS, the client will authenticate the server and the server will authenticate the client.

2. TLSEnable 2-Way-TLS on Service-1 

Lets enable 2-Way-TLS on Service1:

Goto application.properties of Service1 and add below code to enable 2-Way-TLS.

server.ssl.client-auth=need

3. Call Service-1's endpoint from Service2 to see the result

Now, let's run the Service1 and Service2 and hit "http://localhost:8082/withTrustStore" on the browser without doing any other changes.

As you can see in the above image, it failing with "bad_certificate" exception. This indicates that the certificate of the client is not valid because there is no certificate at all. Therefore, we need to create certificate for Service2(i.e. Client) as we did it for Service1 in previous tutorial.

4. Create Self Signed Certificate for Service2

We will generate the self-signed certificate with alias "client_1" for Service2 in same way as we did in previous tutorial. 

keytool -genkeypair \
-alias client_1 \
-keystore keystore_server_2.jks \
-storepass secret \
-keypass secret \
-keyalg RSA \
-keysize 2048 \
-validity 3650 \
-deststoretype pkcs12 \
-ext KeyUsage=digitalSignature,dataEncipherment,keyEncipherment,keyAgreement \
-ext ExtendedKeyUsage=serverAuth,clientAuth

Above command will generate keystore_server_2.jks file with one entry with alias "client_1". To check the certificate entries in the jks file we can use below command:
keytool -v -list -keystore keystore_server_2.jks

5. Start sending certificate from Service2 to Service1

As we created keystore_server_2.jks for Service2 but Service2 is unaware of it. Therefore, we need to tell Service2 to use keystore_server_2.jks while calling Service1 by providing certificate location, keyPass, and storePass password. To do that, first, copy and paste keystore_server_2.jks under the resources/static folder, and second, update the application.properties file of Service2 as below: 

client.ssl.keyStore=classpath:static/keystore_server_2.jks
client.ssl.keyStore.keyPassword=secret
client.ssl.keyStore.storePassword=secret

In StartUpConfig.java, map the newly included above keys with class attributes as shown below.

    @Value("${client.ssl.keyStore}")
    private Resource keyStore;

    @Value("${client.ssl.keyStore.keyPassword}")
    private String keyPassword;

    @Value("${client.ssl.keyStore.storePassword}")
    private String storePassword;

In StartUpConfig.java, previously, we were using only using loadTrustMaterial method for 1-Way-TLS to authenticate Server(Service1), but now, as we have enabled 2-Way-TLS on Service1, we should start sending client certificate(keystore_server_2.jks) to Service1 to authenticate the client(Service2) certificate. To do that, we will modify the SSLContextBuilder to load client Keystore using the loadKeyMaterial method, as shown below. Other things will remain unchanged.

 sslContext = SSLContextBuilder.create()
                    .loadTrustMaterial(trustStore.getFile(),
                            trustStorePassword.toCharArray())
                    .loadKeyMaterial(keyStore.getFile(),
                            storePassword.toCharArray(),
                            keyPassword.toCharArray())
                    .setProtocol(sslVersion)
                    .build();


After doing the above changes, the final output of StartUpConfig.java would be as shown below.

package com.example.service2.config;


import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.ssl.SSLContextBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;

@Configuration
public class StartUpConfig {

    @Value("${client.ssl.trustStore}")
    private Resource trustStore;

    @Value("${client.ssl.trustStore.password}")
    private String trustStorePassword;

    @Value("${client.ssl.version}")
    private String sslVersion;

    @Value("${client.ssl.keyStore}")
    private Resource keyStore;

    @Value("${client.ssl.keyStore.keyPassword}")
    private String keyPassword;

    @Value("${client.ssl.keyStore.storePassword}")
    private String storePassword;

    @Bean
    public RestTemplate getRestTemplate() {
        final SSLContext sslContext;
        try {
            sslContext = SSLContextBuilder.create()
                    .loadTrustMaterial(trustStore.getFile(),
                            trustStorePassword.toCharArray())
                    .loadKeyMaterial(keyStore.getFile(),
                            storePassword.toCharArray(),
                            keyPassword.toCharArray())
                    .setProtocol(sslVersion)
                    .build();
        } catch (Exception e) {
            throw new IllegalStateException("Failed to setup client SSL context", e);
        }
        final HttpClient httpClient = HttpClientBuilder.create()
                .setSSLContext(sslContext)
                .build();
        final ClientHttpRequestFactory requestFactory =
                new HttpComponentsClientHttpRequestFactory(httpClient);
        return new RestTemplate(requestFactory);
    }
}

6. Call Service-1's endpoint from Service2 to see the result

Now, let's compile and run the Service1 and Service2 and hit "http://localhost:8082/withTrustStore" on the browser to test our changes.


As you can see in the above image, it is failing with the "certificate_unknown" exception. Previously, we were getting "bad_certificate" when we were not passing the client certificate. "certificate_unknown" exception indicates that the certificate which the client is sending to the server(Service1) is not present in its TrustStore and hence, unable to authenticate the client(Service2). Therefore, we need to export the client's certificate (Service2) and add it inside Service1's TrustStore.

7. Create and set the trustStore for Service1

Lets export "client_1" certificate present in keystore_server_2.jks to client_server_2.cer using below command.

keytool -v -exportcert \
-file client_server_2.cer \
-alias client_1 \
-keystore keystore_server_2.jks \
-storepass secret -rfc

Create server truststore with certificate of the client by using the below command. Below command will append the client's certificate with alias "client_1" in truststore_server_1.jks truststore file. If truststore_server_1.jks is not there it will create new file. 

keytool -v -importcert \
-file client_server_2.cer \
-alias client_1 \
-keystore truststore_server_1.jks \
-storepass secret \
-noprompt


The Service1(server) is also not aware of the newly created truststore. Therefore, in Service1, append the following properties in current application.properties and copy paste the above newly created truststore_server_1.jks file under resources/static directory.

server.ssl.trust-store=classpath:static/truststore_server_1.jks
server.ssl.trust-store-password=secret


8. Result

Now, let's compile and run Service1 and Service2 and hit "http://localhost:8082/withTrustStore" on the browser to test our changes.


Finally, we are getting expected response without any exception.




As shown in the above image, we have successfully implemented 2-Way-TLS to authenticate counterparts and developed secured communication between Service1(Server) and Service2(Client). However, connection between browser and Service2 is still insecure as we have not enabled 2-Way-TLS or 1-Way-TLS on Service2 Server. Actually, we have created KeyStore and TrustStore for the Rest Template Client(HTTPs Client) to call Service1. 

Normally, we uses 1-way SSL/TLS for securing communication between browser and server because our browser is checking the certificate of the server but the server is not checking for a client certificate pre-installed in your browser. You can enable 1-Way-TLS on Service2 for securing communication between Service2 and Browse by following same steps as we did it for service1 in previous tutorial. 

Comments

  1. KING CASINO, LLC GIVES A $100 FREE BET
    KING CASINO, LLC GIVES A https://febcasino.com/review/merit-casino/ $100 herzamanindir.com/ FREE BET to try. Visit us today and receive a $100 FREE BET! Sign septcasino.com up at our new febcasino site! https://septcasino.com/review/merit-casino/

    ReplyDelete
  2. Merkur 15c Safety Razor - Barber Pole - Deccasino
    Merkur ventureberg.com/ 15C Safety Razor - Merkur - 15C for Barber wooricasinos.info Pole หารายได้เสริม is https://deccasino.com/review/merit-casino/ the perfect introduction https://vannienailor4166blog.blogspot.com/ to the Merkur Safety Razor.

    ReplyDelete

Post a Comment