AES-GCM Encryption Utilities for React Application

Uchenna Awa
3 min readDec 21, 2023

--

This document provides an overview of a series of JavaScript utility functions for handling AES-GCM symmetric encryption and decryption, tailored for web applications. These utilities include functions for key generation, encrypting and decrypting data, and storing or retrieving encrypted data from the browser’s local storage.

This functions can aid in encrypting data before storing to local storage on a client-side application.

Functions

1. Key Generation

Generates a cryptographic key suitable for AES-GCM encryption and decryption from a base64 encoded string.

const makeKey = async (key) => {
return await crypto.subtle.importKey(
"raw",
Buffer.from(key, "base64"),
{
name: "AES-GCM",
length: 256,
},
true,
["encrypt", "decrypt"]
);
};

2. Symmetric Encryption

Encrypts plaintext using the AES-GCM algorithm. Returns an object containing the base64 encoded ciphertext and the initialization vector (IV).

export const encryptSymmetric = async (plaintext) => {
const key = process.env.NEXT_PUBLIC_ENCRYPTION_KEY;
const iv = crypto.getRandomValues(new Uint8Array(12));
const encodedPlaintext = new TextEncoder().encode(plaintext);
const secretKey = await makeKey(key); // using the

const ciphertext = await crypto.subtle.encrypt(
{
name: "AES-GCM",
iv,
},
secretKey,
encodedPlaintext
);

return {
ciphertext: Buffer.from(ciphertext).toString("base64"),
iv: Buffer.from(iv).toString("base64"),
};
};

3. Symmetric Decryption

Decrypts a base64 encoded ciphertext using the AES-GCM algorithm. Requires the ciphertext and the IV used during encryption.

export const decryptSymmetric = async (ciphertext, iv) => {
const key = process.env.NEXT_PUBLIC_ENCRYPTION_KEY;
const secretKey = await makeKey(key);

const cleartext = await crypto.subtle.decrypt(
{
name: "AES-GCM",
iv: Buffer.from(iv, "base64"),
},
secretKey,
Buffer.from(ciphertext, "base64")
);
const data = new TextDecoder().decode(cleartext);
return data;
};

4.Save Encrypted Data to Local Storage

Encrypts and stores data in the browser’s local storage. The data is first “stringified”, then encrypted, and finally stored as a string.

export const saveToLocalStorage = async (name, data) => {
const stringified_data = JSON.stringify(data);
const encrypted_data = await encryptSymmetric(stringified_data);
localStorage.setItem(name, JSON.stringify(encrypted_data));
};

5. Retrieve and Decrypt Data from Local Storage

Fetches encrypted data from local storage, decrypts it, and parses it back into its original object format.

export const getFromLocalStorage = async (name) => {
const raw_data = localStorage.getItem(name);
if (!raw_data) return null;

const encrypted_data = JSON.parse(raw_data);
const decrypted_data = await decryptSymmetric(
encrypted_data.ciphertext,
encrypted_data.iv
);

const un_stringified_data = JSON.parse(decrypted_data);
return un_stringified_data;
};

6. Remove Data from Local Storage

Deletes an item from the browser’s local storage by its key.

export const removeFromLocalStorage = (name) => {
localStorage.removeItem(name);
};

Usage

These utilities are particularly useful in web applications using next JSor reactJS in general where securing client-side data is crucial. They provide a means to encrypt sensitive data before storing it in the browser and ensure that it can be securely decrypted and used when needed.

To incorporate the encryption utility in a React component for saving data, you can import the saveToLocalStorage function from the encryption module as demonstrated in the following example:

import { saveToLocalStorage } from "./encrypter";

const signUpData = {
firstName: first.trim(),
lastName: last.trim(),
};

saveToLocalStorage("nameOfLocalStorageKey", signUpData);
application local storage with encrypted value

For utilizing the decryption utility in a React component, you can import the getFromLocalStorage function. Here is an example that demonstrates how to fetch and decrypt data stored in local storage:

import React, { useEffect, useState } from "react";
import { getFromLocalStorage } from "./encrypter";

const MyComponent = () => {
const [clientInfo, setClientInfo] = useState(null);

useEffect(() => {
const fetchClientInfo = async () => {
const info = await getFromLocalStorage("nameOfLocalStorageKey");
setClientInfo(info);
};
fetchClientInfo();
}, []);

const { firstName, lastName } = clientInfo || {};

// Rest of your component logic…
return (
// Your JSX code…
);
};

In this example, getFromLocalStorage is used within a useEffect hook to asynchronously fetch and decrypt data from local storage when the component mounts. The decrypted data is then stored in the component’s state. This approach ensures that the data is available for use within the component once it has been successfully fetched and decrypted.

The complete code is available here.

--

--