Angular : Achieving payload encryption with Forge | Node-Forge

Angular : Achieving payload encryption with Forge | Node-Forge

 


Node-forge is a very famous Javascript library which provides a set of utilities for making implementation of cryptography standards easier.

It abstracts the heavy work and exposes simple functions which we can use to get the job done. It provides implementation for most of the popular ciphers and PKIs.

 

Norge-forge is really fast and doesn’t impact performance of our application. You can check the official NPM page at https://www.npmjs.com/package/node-forge.

Today I will explain how you can use node-forge in your Angular project to implement payload encryption. Of course at the back-end you APIs need to have the corresponding logic, so that process of encryption and decryption can work.

 I will focus on Angular here, in back-end you are free to use any technology.

Need of payload encryption:

To make the communication between client and server a secure one and protect the data from alteration or eavesdropping.

 

Concept: 

Angular communicates with Back-end through HTTP Client which sends and receives JSON responses.

In this tutorial we will use AES 256 encryption to encrypt our JSON payload. Key for encryption can be generated using forge API.

 

Steps:

1. Installing node-forge in your project

In your project directory run the below command which will download and save node-forge in your project

npm install --save node-forge

 

2. Generating random, AES Key and IV values.

Using the built-in forge API we can generate key and IV as below

 

    import * as forge from 'node-forge'; 

let iv = forge.random.getBytesSync(32); // IV  

    let aesKey = forge.random.getBytesSync(32); // KEY  }

Here we have taken 32byte AES key and IV values, which can bring in enough randomness and security.

 

3. Encrypting payload with AES

You can create a Encryption utility to decouple the encryption-decryption process from interceptor. Below snippet which I wrote can be used for the same. Javascript objects can be converted to plainText easily using JSON.stringify( ).

 

 encryptAES (aesKey stringivstringplainTextstring): string {    const cipherDatathis.getCipher(aesKey ); // Create a cipher with key and IV    cipherData.start({

      iv

      tagLength: 128 // Authentication tag

    });

    cipherData.update(forge.util.createBuffer(plainText));

    cipherData.finish();

    const encrypted = cipherData.output;

    const tag = cipherData.mode.tag;

     return forge.util.encode64(encrypted.data + tag.data));

  }

We return the encrypted cipher text with tag appended to it. The Back-end/ server side implementation should implement decryption logic accordingly with authentication of Tag.

 

4. RSA encrypting AES key and IV with RSA

 Now our payload is encrypted with AES. Along with payload we need to send Key and IV values to server, which it will use for decryption.

For that we will use server’s public key and encrypt using RSA API provided by forge. The public key will be a base 64 encoded value (for e.g. contents of a PEM file).

 

 encryptRSA (msgstring): string { // payload can be key and IV

      const publicKey = forge.pki.publicKeyFromPem(“server public key”);

      const buf = forge.util.createBuffer(forge.util.encode64(msg), 'utf8');

     const encrypted = publicKey.encrypt(buf.bytes(), 'RSA-OAEP');

    return forge.util.encode64(encrypted);

  }

  

5. Updating request inside interceptor (cross-cutting concern):

Like in normal angular HTTP interceptor, we modify the captured request so that the encryption logic can be decoupled from the business logic

// SAMPLE REQUEST CAN BE LIKE : 

  request = request.clone({

        requestPayload: AES Encrypted Payload,

     aesKey: RSA encrypted AES Key,

     iv: RSA encrypted AES IV

 });


6. Decrypting the response from Server

Once server has process the request it can use the same AES Key and IV to encrypt its response and send back. Or it is free to generate new pair and encrypt the response and send it. We will consider that server uses same key and IV pair.

 

decryptAES (encryptedTextstringkeystringivstring): string | null {

    try {

      const byteString = forge.util.decode64(encryptedText);

      const authTag = byteString.slice(byteString.length - 16); // 128 bits TAG

      const encryptedTxt = byteString.slice(0byteString.length - 16); // Extract text without TAG

      const decipherData = this.getDecipher(key);

      decipherData.start({

        iv,

        tagLength: 128,

        tag: authTag,

      });

      decipherData.update(forge.util.createBuffer(encryptedTxt));

      const pass = decipherData.finish();

      if (pass) {

         return decipherData.output.data;

      }

    } catch (error) {

      console.log(error);

    }

    return null;

  }

 

BONUS: 

Question may arise, why use two different encryption mechanism? Why not directly use RSA for encrypting payload with Server’s public key.

Because PKIs like RSA are slow, encrypting large payload will cost us performance. Apart from this there is limitation on RSA that it can encrypt amount of data equal to the key size used. In case if any padding/header data is used that also will need to be subtracted from the key size to arrive at the size of payload that can be encrypted with RSA at once.  

That is why we are using symmetric key algorithm e.g. AES for payload which does not suffer from this disadvantage. For fixed/small length Key and IV pair we are using RSA.


That's all folks in this post. Take Care. In case you need any clarification, please let me know in comments.


Comments