Authenticating and Securing Communication between Micro-Services Part-1

This tutorial explains to you about authenticating and securing internal communication between micro-services using 2-Way-TLS authentication. Before understanding 2-way-TLS, you must know 1-way-TLS, its handshake process, usage, creating self-signed certificates & implementation. So in this tutorial series, we have covered everything so the learner can directly move from novice to expert.


Table of Content:

  1. What is TLS?
  2. TLS v/s SSL
  3. Terminology
  4. A brief explanation of the 1-Way-TLS handshake process with a diagram
  5. Where 1-Way-TLS is used? 
  6. Creating Service-1 using SpringBoot with one endpoint(without TLS/Encryption)
  7. Creating Self Signed Certificate
  8. Enable HTTPS on Service1 i.e. Enable 1-Way-TLS
  9. Result of hitting the endpoint using a browser without installing the certificate on a client machine
  10. Result of hitting the endpoint using browser after installing the certificate on a client machine
  11. Creating Service2 using SpringBoot and call Service-1's endpoint from Service2

Github Link: 

Service1 : https://github.com/joyshah/service1 (Branch Name:main
  • Git clone https://github.com/joyshah/service1.git
Service2 : https://github.com/joyshah/service2 (Branch Name:main
  • Git clone https://github.com/joyshah/service2.git

1. What is TLS?

    TLS means Transport Layer Security, a cryptographic protocol designed to facilitate end-to-end security and privacy of data sent over network between applications. It is used to encrypt communication between client & server or between two servers. The most recent version of TLS is 1.3 published in 2018.


2. TLS v/s SSL?

    TLS is an improved version of SSL(Secured Socket Layer). SSL word has become so common that even today we use SSL for end-to-end encryption but actually, we are using TLS. The below image depicts everything.



3. Terminology

  1. Certificate: A certificate contains a public key and a name. The certificate may also have an expiration date, the name of the certifying authority that issued the certificate, organization info to which certificate issued, a serial number, encryption algorithm details and optional additional information. Most importantly, it contains the digital signature of the certificate issuer. 
  2. KeyStore: A KeyStore stores server keys i.e. public key and private key, along with the signed certificate. We can store a list of server keys, along with the signed certificate. It is required only when a server is running on a TLS connection. It is used to prove its own identity to its counterpart.
  3. TrustStore: A TrustStore is kind of KeyStore which stores one or more certificate also knows as a public key. It is used to store the certificates of trusted entities and to verify a counterpart's identity. A process can maintain a store of certificates of all its trusted parties which it trusts.

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

    When SSL/TLS connection is established between a client and a server, it follows several steps shown in the below image.


Establishing a TCP/IP Connection with SSL/TLS Session


TCP Connection: A TCP connection is established between client and server by the following process.
  1. The client will send a SYNC J request to the server to initiate TCP Connection.
  2. The server will respond with SYNC K, ACK J+1 response. 
  3. The client will respond to the server with an ACK K+1 response.
After invoking the above steps, a client and server can exchange information. SSL/TLS can be used for other protocols as well but the most common use case is over TCP/IP due to its ubiquity, reliability of transport, and ability to recover from lost packets and transport errors.

SSL/TLS Handshake: An SSL/TLS handshake process starts once a TCP connection is established. It involves the below steps:
  1. The client sends a "ClientHello" message, which includes the version of SSL/TLS a client is capable of, what cipher suites it supports, and any compression types available.
  2. The server responds to the client with "ServerHello" which includes the same information as the client and sends the server's certificate to the client. Server certificate includes information such as Organization Information, Issuer Information, Certificate Validity, Public Key, Signature, Encryption Algorithm Details & other optional information.
  3. The client verifies the server certificate is valid by using the CA certificate present in its KeyChain(Mac) or Microsoft Management Console(Windows), we can say that this is the browser's trust store. Note, in this part, we are not going to use CA(Certificate Authority) rather we will export the self signed server certificate itself in the trust store. By this process, we can make sure that the server is authenticated and the imposter is not conducting the man-in-middle attack. 
  4. If the certificate is found valid, the client will generate a pre-master key, encrypt it with Server Certificate's Public Key and send it to the server. Please note pre-master key is not a session key. The session key is never transmitted between client and server, but it's generated by the client and server at their end using Diffie-hellman or variants of the Diffie-hellman algorithm.
  5. The server will decrypt the pre-master key shared by the client using the server certificate's private key, which would be only with the server. Now the server will generate the session key using the decrypted pre-master key and use it for further communication with the client. The client generates a session key as it already has the pre-master key.
  6. Now that the handshake has been established and the session key has been exchanged, data exchange between client and server can be encrypted and decrypted using the shared session key. This symmetric cryptography will secure the remainder of application-to-application communication.
The main purpose of asymmetric cryptography is to exchange symmetric cryptography keys. Asymmetric cryptography is enormously slow, compared to symmetric encryption, due to the longer key lengths and the complexity of the encryption algorithms used. Therefore, further communication between client and server is carried out using symmetric cryptography after session key exchange.

5. Where 1-Way-TLS is used?

    Let's take a naive example, whenever "www.google.com" is fired from the browser, TCP connection with SSL/TLS handshake is established.

 As shown in the below image, the server responds with SSL/TLS Server Certificate.

As shown in the below image, the SSL/TLS Server Certificate includes Organization Information, Issuer Information, Certificate Validity, Public Key, Signature, Encryption Algorithm Details & other optional information.


The SSL/TLS Server Certificate received from the server is verified using certificate authority GTS Root R1 present in KeyChain as shown in the below image. GTS Root R1, DigiCert High Assurance EV Root CA, and many such CA's are Certificates issued by Certificate Authority which comes pre-install with the system. It is verified using the chain of trust approach; I will be covering that topic in detail in another post. In this tutorial, we will be using self-signed certificates without certificate authority.


6. Creating Service-1 using SpringBoot with one endpoint(without TLS/Encryption) 

  1. https://start.spring.io/ 
  2. Give the "Service-1" as artifact name 
  3. Click on add dependencies button and select spring web dependency.
  4. Keep other things default and click on generate button. 
  5. Create a package name with controller and a class SSLTestController.java, and paste the below code to create an endpoint. Or you can clone it from "https://github.com/joyshah/service1"

package com.example.service1.contoller;

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

@RestController
public class SSLTestController {

    @GetMapping
    public String getData() {
        return "Hello World, This is Service 1";
    }
}

Hitting http://localhost:8081(In my case, I am using 8081 port, you can change the port by the overriding server.port=8081 in the application.properties file) in the browser will return the above data as shown above.

Clicking on the exclamatory icon will show you the information about SSL/TLS certificate. It is showing unsecured as SSL/TLS is yet not set up.

7. Creating Self Signed Certificate

    To create a self-signed certificate, we will be using the below KeyTool command which will generate the "keystore_server_1.jks" file. Let's understand the keytool command options used for generating server certificates:

  1. genkeypair option of KeyTool means generate a key pair that is private and public key
  2. KeyStore option is used to name generated jks file. Here you can provide any file name.
  3. JKS file can contain more than one key pair/certificate, so an alias is used to reference that exact key pair in the KeyStore.
  4. storepass and keypass are passwords. storepass is used to access KeyStore by using you can all the information of certificate except "key pair's private key" while keypass is used to get particular key pair's private key. 
  5. keyalg specifies the algorithm to be used to generate the key pair, and keysize specifies the size of each key to be generated.
  6. Validity is the validity of the certificate in days which generates two fields to "Not Valid Before" and "Not Valid After".

Command:

    keytool -genkeypair \
    -alias server_1 \
    -keystore keystore_server_1.jks \
    -storepass secret \
    -keypass secret \
    -keyalg RSA \
    -keysize 2048 \
    -validity 3650 \
    -deststoretype pkcs12 \
    -ext KeyUsage=digitalSignature,dataEncipherment,keyEncipherment,keyAgreement \
    -ext ExtendedKeyUsage=serverAuth,clientAuth \
    -ext SubjectAlternativeName:c=DNS:localhost,IP:127.0.0.1    

Please note that the JKS format is a proprietary format and commonly used with java world. A different open-source format such as PCKS12 is open source and is not specific to java. However, it is possible to convert formats.

After running the above command on the terminal, it will ask the below questions. You can fill in your details or can use the below answers but make sure you enter 'YES' for the last question, else it will ask for all the information again.
  1. What is your first and last name? -> Joy Shah
  2. What is the name of your organizational unit?  -> microservicetutorial org
  3. What is the name of your organization? -> microservicetutorial org
  4. What is the name of your City or Locality? -> Bangalore
  5. What is the name of your State or Province?  -> Karnakata
  6. What is the two-letter country code for this unit? -> IN
  7. Is CN=Joy Shah, OU=microservicetutorial org, O=microservicetutorial org, L=Bangalore, ST=Karnakata, C=IN correct? -> Yes


Once the above process completes, you will find "keystore_server_1.jks" in your current directory. To see the content inside the JKS file, we can use the below command. It will list all the certificates present in the JKS file. Once you execute the below command, it will ask you for the KeyStore password that is the StorePass password you gave while generating the JKS file.

keytool -v -list -keystore keystore_server_1.jks



8. Enable HTTPS on Service1 i.e. Enable 1-Way-TLS

     To enable 1-Way-TLS/HTTPS in Service1, we have to enable the SSL server and need to add certificate information such as certificate path, StorePass, and KeyPass as shown below in application.properties file. You can copy and paste generated keystore_server_1.jks under resource/static directory.

server.port=8081
server.ssl.enabled=true
server.ssl.key-store=classpath:static/keystore_server_1.jks
server.ssl.key-password=secret
server.ssl.key-store-password=secret

9. Result of hitting the endpoint using a browser without installing the certificate on a client machine

Hitting without appending 'S' in "HTTP"(http://localhost:8081) will give you the following error because service1 is expecting a TLS handshake which would not take place in the case of HTTP.



Now, you might be wondering then how "http://www.google.com/" is working? So, in this case, google is redirecting "http://www.google.com/" to "https://www.google.com/" as shown below:


Hitting after appending 's' in "http"(https://localhost:8081) will give you expected data.


Even after enabling TLS on service1 and started hitting URL with HTTPS still, we are getting the "not secure" exclamatory icon because the browser is unable to authenticate the certificate as the certificate is not present in KeyChain(Mac) or Microsoft Management Console(Windows). In this scenario, even though all the communication will be encrypted and secure, but we could not authenticate the responding server, that is to say, we could not distinguish response is from an actual real server or response is from an imposter/Man-In-Middle.


As you can see in the below image, the server responded with a server certificate, but as I said earlier, we don't have a certificate to authenticate the server certificate is from the actual server or from the impersonator. 



10. Result of hitting the endpoint using browser after installing the certificate on a client machine

    In order to install the certificate on the client machine, first, we need to export self-signed server certificate to the client. Let's understand the keytool command options used for exporting certificate:
  1. --exportcert option of keytool is used for exporting the certificate. 
  2. -file option is to specify a name for the newly generated certificate file. 
  3. -keystore defines the server certificate, which has public as well private keys, from which we will export a server certificate without the private key. 
  4. -storepass is a password to access the keystore file that is keystore_server_1.jks file.
  5.  -alias is used to refer particular certificate from the list of certificates present in keystore_server_1 .jks file. 
  6. -rfc option makes the certificate printable as RFC 1421 specification. You can open .cer file even in text editors.

Export Certificate of server in PEM format:
 keytool -v -exportcert \
-file server_1_certificate.cer \
-alias server_1 \
-keystore keystore_server_1.jks \
-storepass secret -rfc


Let's install server_1_certificate.cer in machine:
    
1.  Open KeyChain application, click on file and select "import Items" option. For windows, you can open MMC i.e. Microsoft Management Console and do the same.


   
2.  Select the server_1_certificate.cer.



3.  Once you import the certificate, double click on imported certificate to open certificate detail window.


4.  Under trust section, select "Always Trust" option and close.




Now, let's hit https://localhost:8081 in the browser.

Hurrah!!! Started getting a secure icon. Now, clicking on the lock icon will show us "Connection is secure", which means we have successfully completed 1-Way-TLS. The browser has successfully authenticated the responding server and now, we are sure that the obtained response is from the actual server and is not tampered by Impersonator/Man-In-Middle.


11. Create Service2 using SpringBoot and calling Service-1's endpoint

Let's create Service2 first to call SSL/TLS enabled endpoints of Service1:
  1. https://start.spring.io/ 
  2. Give the "Service-2" as artifact name 
  3. Click on add dependencies button and select spring web dependency.
  4. Keep other things default and click on generate button. 
  5. Create a package with name "controller" and inside that create a class namely SSLTestController.java, and paste the below code to create an endpoint. Or you can clone it from "https://github.com/joyshah/service2"

package com.example.service2.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class SSLTestController {
@GetMapping("/withoutTrustStore")
public String getDataFromService1WithoutTrustStore() {
RestTemplate restTemplateWithoutTrustStore = new RestTemplate();
try {
return restTemplateWithoutTrustStore.getForEntity("https://localhost:8081", String.class).toString();
} catch (Exception e) {
return e.getMessage();
}
}
}

     6. Let's change default port(8080) to 8082 by adding below line in application.properties. Make sure this port is different from Service1.

server.port=8082


Now, let's hit "http://localhost:8082/withoutTrustStore" in the browser.


As you can see, we got SunCertPathBuilderException. It means that the client(i.e. Service2) wants to communicate over HTTPS and during the handshake procedure it received the certificate of the server which it doesn't recognize yet. It's the same scenario as we experienced in the 9th point where the browser is unable to validate/authenticate/recognize the certificate of the server (i.e. Service1). However, the browsers will still allow you to open the said website without a certificate with an insecure exclamatory symbol while programs don't. So to resolve this issue, we exported the server(i.e. Service1) certificate and imported it into the keychain. Similarly, we have to do the same programmatically.

let's first convert the certificate file(server_1_certificate.cer) to truststore_server_2.jks using the below command:
keytool -v -importcert \
-file server_1_certificate.cer \
-alias server \
-keystore truststore_server_2.jks \
-storepass secret

Now, we need to create a RestTemplate with custom SSLContext using truststore_server_2.jks, its password & TLS Version.

Let's modify the application.properties to add truststore_server_2.jks path, password, and TLS version type as shown below and copy & paste the generated truststore_server_2.jks into resource/static directory of Service2.

server.port=8082
client.ssl.trustStore=classpath:static/truststore_server_2.jks
client.ssl.trustStore.password=secret
client.ssl.version=TLSv1.3

Now, let's create a package namely "config", under that, create a configuration class called StartUpConfig.java. Read the required values using @Value Annotation from the application.properties file and create restTemplate beans using @Bean Annotation. We are using SSLContextBuilder with path, password, and TLS type to build the SSLContext object. Using the SSLContext object, we are creating a HttpClient object with the help of HttpClientBuilder. We are creating ClientHttpRequestFactory using HttpClient which inturns used for creating restTemplate object.

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;

    @Bean
    public RestTemplate getRestTemplate() {
        final SSLContext sslContext;
        try {
            sslContext = SSLContextBuilder.create()
                    .loadTrustMaterial(trustStore.getFile(),
                            trustStorePassword.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);
    }
}

Create a new endpoint ('/withTrustStore") in SSLTestController in Service2 as shown below which will be using above restController with customer SSLContext.

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/withTrustStore")
    public ResponseEntity<String> getDataFromService1OverTLS() {
        return restTemplate.getForEntity("https://localhost:8081", String.class);
    }


Now, let's hit "http://localhost:8082/withTrustStore" in the browser.

Woohoo! Finally, the Service2(i.e. Client) is able recognize/authenticate the Service1(i.e. TLS enabled Server) and we are getting response successfully from Service1 to Service2 and from Service2 to browser.




As shown in the above figure, we have not turned on SSL/TLS for Service2 but SSL/TLS is enabled for Service1. Hence, the communication between Service2 and Service1 is authenticated and encrypted using a self-signed certificate but it's insecure between Browser and Service2. We can carry out steps no.7 to 10 for Service2 to enable SSL/TLS.


Comments

  1. A look at the best free slot machines - Dr.MCD
    Free slot machines from the list of 제천 출장마사지 the 영천 출장안마 best free casinos online 시흥 출장마사지 today. Check out our 보령 출장샵 guide on the best free casinos and slot 경상북도 출장샵 machines of 2021.

    ReplyDelete

Post a Comment