2 Way SSL Encryption on Google App Engine

“Any communication is a bridge holding 2 different ends together. People will trust that bridge only if it is safe & reliable.”

In a vast virtual world of the internet, information security has become an utmost priority & of importance. On a daily basis, you end up communicating with a lot of known & unknown entities. You would never want your personal or financial information to be tracked by a malicious user/entity. The pertinent question is, How do I make my communication secure over the open internet when I am using Google App Engine as my application deployment server? Solution to this problem is a SSL encrypted communication channel between the GAE application & the external world.

Now you might ask, what exactly is GAE & what does SSL enabled communication really mean? Here is a sneak peek into both these concepts. GAE stands for Google app engine service provided by GCP. It is a serverless deployment environment wherein you do not need to manage the server hardware, OS or runtime configurations, GCP will take care of it for you. While GAE manages your server for you, configuring & managing a Two-way SSL communication is still your responsibility to manage.

SSL stands for Secure Socket Layer. It first establishes the handshake between the 2 communicating entities by using a pair of private & public keys(certificates). No communication will be initialised until this handshake is completed. On a successful completion of handshake the sender of the data will encrypt the data by using it’s private key, while the receiver will decrypt it by using it’s paired public key. There are 2 types of SSL communication channels, One-way SSL communication & Two-way SSL communication channel. We will focus only on Two-way SSL communication in this post. This post expects you to understand the basics of Google app engine, SSL & last but not least http communication by using java.

What is Two-way SSL Communication & its Advantages?

Before we delve into the details of SSL enabled encrypted communication, let’s first understand how an unencrypted communication looks like.

How about encrypted communication?

Two-way SSL communication dictates that,

  • Both server & client applications will get their respective pair of public & private certificates signed by a certificate authority.

  • They will exchange their respective public certificates with each other. It will then be stored in their respective truststores.

  • In very simplified terms, clients will first send the details of supported ciphers & SSL/TLS versions to the server system. Server will then share it’s public certificate with the client.

  • Client will validate this certificate against the one which has been stored in the truststore. On successful validation it will send it’s public certificate to the server.

  • On successful validation of the client’s public certificate at the server end, a handshake will be marked complete.

  • No communication channel will be established & initiated unless the handshake is complete.

  • Both parties will store their private keys in their keystores & exchange the public keys with each other which will be stored in their respective truststores.

  • Once the handshake is established both ends will encrypt their data by using their respective private key which can only be decrypted by using it’s paired public key ensuring that no one else will get hold of the request data.

What Exactly is a Keystore & Truststore?

As its name suggests it’s a holder to store the private keys & public certificates. Any consumer of these keys & certificates can simply pull it out of these stores. Keystore & truststore can be used out of the OS keystores & truststores(isn’t applicable for GAE), in-memory stores or via GCP secret management.

Enough theory for now, let’s see how it can be done in real life.

How can I Update my Keystore with the Certificate & Private Key?

  • Creating a keystore by using the client certificate & private key is pretty easy & straightforward. First we will need to create a PKCS12 format keystore containing both client certificate & private key.

 

openssl pkcs12 -export -in CLIENT_CONCATENATED_CERTIFICATE.crt -inkey CLIENT_PRIVATE_KEY.key -out CLIENT_PKCS12_CERTIFICATE.p12

  • Import the above created PKCS12 keystore file into JKS keystore by using below command. Here JKS stands for Java KeyStore.

 

keytool -importkeystore -srckeystore CLIENT_PKCS12_CERTIFICATE.p12 -srcstoretype PKCS12 -destkeystore CLIENT_JKS_CERTIFICATE.jks -deststoretype JKS

What about the Truststore? How can I Prepare for it?

  • Get a remote/target host’s public certificate which you will be storing in your truststore to verify the validity of the target host & it’s connection request. Most probably it will be .cer or .cert file.

  • To add this certificate to a truststore you will need to provide a unique alias name to this new certificate. You can list down all the existing aliases embedded in the current truststore by using the below command.

 

keytool -v -list -keystore PATH_OF_THE_TRUSTSTORE_FILE(usually jks file)

 

  • Once you have a server certificate & it’s unique alias name ready, all you have to do is import these public certificates into your truststore. Remember your public certificate could be a chain of certificates instead of a single certificate. In such cases you will have to create a single merged certificate file & then import it.

 

keytool -importcert -file PUBLIC_CERTIFICATE_NAME.cer -keystore CLIENT_TRUSTSTORE_NAME.jks -alias NAME_TO_IDENTIFY_THIS_CERTIFICATE_INTO_KEYSTORE_USUALLY_DOMAIN_NAME

How can I Address the Above Issue?

  • Use GCP’s secret manager instead.

  • It can not only hold onto all your certificates but also passwords & other secure data.

  • Pushing your keystore/truststore files to GCP’s secret manager is as simple as invoking a single command provided that you have all the appropriate APIs & IAM permissions enabled.

 

gcloud secrets create certificate \
–data-file=KEYSTORE_FILE_PATH \
–labels=my-credentials-type=certificate \
–replication-policy=automatic \
–project=PROJECT_NAME

 

  • Whereas reading from the Secret Manager version is equally easy, thanks to GCP’s Secret Manager SDK. Just a couple of api calls & you have all your sensitive information at your fingertips.

 

try (SecretManagerServiceClient client = SecretManagerServiceClient.create()) {
SecretVersionName secretVersionName = SecretVersionName.of(projectId, secretId, versionId);

// Access the secret version.
AccessSecretVersionResponse response = client.accessSecretVersion(secretVersionName);
String payload = response.getPayload().getData().toStringUtf8();
}

 

  • Just like keystore & truststore files you can also store & refer all the store passwords & security keys.

Time to cross one last hurdle, using these Keystore & truststore in our communication code.

 

// Initiate KeyManager array with all the available keystores
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
KeyStore keystore = KeyStore.getInstance(“JKS”);
keystore.load(keystoreStream, “KEYSTORE_PASSWORD_PROVIDED_WHILE_CREATING_KEYSTORE”.toCharArray());
kmf.init(keystore,”KEY_PASSWORD_PROVIDED_WHILE_CREATING_KEYSTORE”.toCharArray());
KeyManager[] keyManagers = kmf.getKeyManagers();
keystoreStream.close();
keystoreStream = null;

// Initiate TrustManager array with all the available keystores
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore truststore = KeyStore.getInstance(“JKS”);
truststore.load(truststoreStream, “KEYSTORE_PASSWORD_PROVIDED_WHILE_CREATING_KEYSTORE”.toCharArray());
tmf.init(truststore);
TrustManager[] trustManagers = tmf.getTrustManagers();
truststoreStream.close();
truststoreStream = null;

// Once we have both the TrustManager & KeyManager array initiated, let’s initiate the SSLContext & provide it to our http client.
SSLContext sslContext = SSLContext.getInstance(“SSL”);
sslContext.init(keyManagers, trustManagers, null);
if (urlConnection instanceof HttpsURLConnection) {
HttpsURLConnection httpURLConnection = (HttpsURLConnection) urlConnection;
httpURLConnection.setSSLSocketFactory(sslContext.getSocketFactory());
httpURLConnection.connect();

// Congratulations! You have just secured the world of information!
}

Conclusion

At first you may find setting up the keystores, truststores a bit confusing & difficult. However, with a little bit of basic understanding of SSL communication & GCP’s secret manager, setting it up is a piece of cake. It will not only ensure communication security but also restore your peace of mind. I hope you enjoyed reading this post!


Disclaimer – The blog is written by one of the finalists of an internal blogathon competition we held recently at Dista.

The author of this article is Rupesh Chavan, Tech Architect.