Shielding Your Data: AES-GCM Encryption & Decryption for JavaScript, TypeScript, Java, and Python ๐Ÿ›ก๏ธ๐Ÿ”

Shielding Your Data: AES-GCM Encryption & Decryption for JavaScript, TypeScript, Java, and Python ๐Ÿ›ก๏ธ๐Ÿ”

ยท

7 min read

Hey There, ๐Ÿ‘‹ Awesome Developers! ๐Ÿš€ Today, let's explore the AES-GCM encryption and decryption with code examples in JS, TS, Java and Python.

If you're eager to enhance your data protection skills! ๐Ÿ‘‡ Let's get started!

Introduction ๐Ÿš€

Ever felt worried about keeping your digital secrets safe? In today's digital world, it's super important to keep our data safe and secure. Cryptography is like the secret code language that helps us do that.

One of the most popular ways to keep information safe is through encryption. Encryption is like turning your message into a secret code so only the person with the key can read it.

AES-GCM is one type of encryption that's really good at keeping data safe. It not only locks up the information so no one can read it without permission, but it also makes sure nobody messes with it while it's locked up.


What is AES-GCM? ๐Ÿ”

Interestingly, AES-GCM is a love child of two powerful technologies โ€” the Advanced Encryption Standard (AES) and the Galois/Counter Mode (GCM).

The AES provides a strong 'symmetric key cipher' that locks your information in a secure coffer. Meanwhile, the GCM wraps this encrypted information in an additional protective layer that ensures data authenticity.

In layman's terms, it's like receiving a well-wrapped present. Not only is the gift (your data) inside the box secure, but the wrapping (GCM) on the outside also ensures it's the real deal, undisturbed until it reaches you.


But, Why AES-GCM? ๐Ÿค”๐Ÿ”

  1. Fast and Secure: AES-GCM is more efficient and secured compared to other encryption algorithms.

  2. Widely Adopted: It is widely used in various applications and is supported by most modern systems and devices.

  3. High Performance: AES-GCM provides high performance encryption and decryption capabilities.

Did you know that the AES-GCM algorithm is commonly used in securing Wi-Fi networks and TLS (Transport Layer Security) connections on the internet? It's trusted by millions of users worldwide to keep their communications private and secure.


Cross-Language AES-GCM Encryption ๐Ÿ”๐ŸŒ

I am providing the code below for you use. Feel free to utilize it in your application. Whether you prefer encrypting data in the backend (using Java/NodeJS/Python) and decrypting it in the frontend using JavaScript, or vice versa, this solution accommodates both scenarios seamlessly.

In simple words,

Encrypt where you will, Decrypt where you choose.

This cross-language compatibility highlights the flexibility and robustness of AES-GCM encryption across various programming environments.


Javascript - The language of the web ๐ŸŒ๐Ÿ’ป

class AESGCM {
    static ALGORITHM = "AES-GCM";
    static HASH = "SHA-256";
    static KEY_SIZE = 256;
    static ITERATION_COUNT = 100000;

    constructor() {
        this.textEncoder = new TextEncoder();
        this.textDecoder = new TextDecoder();
    }

    async generateKeyMaterial(password) {
        return crypto.subtle.importKey(
            "raw",
            this.textEncoder.encode(password),
            { name: "PBKDF2" },
            false,
            ["deriveBits", "deriveKey"]
        );
    }

    async generateKey(keyMaterial, salt) {
        const algorithm = AESGCM.ALGORITHM;
        const hash = AESGCM.HASH;
        const iterationCount = AESGCM.ITERATION_COUNT;
        const keySize = AESGCM.KEY_SIZE;

        return crypto.subtle.deriveKey(
            {
                name: "PBKDF2",
                salt,
                iterations: iterationCount,
                hash: hash
            },
            keyMaterial,
            { 
                name: algorithm,
                length: keySize 
            },
            true,
            ["encrypt", "decrypt"]
        );
    }

    async encrypt(plaintext, password) {
        const data = this.textEncoder.encode(plaintext);
        const salt = crypto.getRandomValues(new Uint8Array(16));
        const keyMaterial = await this.generateKeyMaterial(password);
        const key = await this.generateKey(keyMaterial, salt);
        const algorithm = AESGCM.ALGORITHM;
        const iv = crypto.getRandomValues(new Uint8Array(12));
        const encryptedData = await crypto.subtle.encrypt(
            {
                name: algorithm,
                iv
            },
            key,
            data
        );

        const ciphertextBase64 = btoa(String.fromCharCode.apply(null, new Uint8Array(encryptedData)));
        const ivBase64 = btoa(String.fromCharCode.apply(null, iv));
        const saltBase64 = btoa(String.fromCharCode.apply(null, salt));

        return ciphertextBase64 + ":" + ivBase64 + ":" + saltBase64;
    }

    async decrypt(encryptedData, password) {
        const [ciphertextStr, ivStr, saltStr] = encryptedData.split(":");
        const ciphertext = Uint8Array.from(atob(ciphertextStr), c => c.charCodeAt(0));
        const iv = Uint8Array.from(atob(ivStr), c => c.charCodeAt(0));
        const salt = Uint8Array.from(atob(saltStr), c => c.charCodeAt(0));
        const algorithm = AESGCM.ALGORITHM;
        const keyMaterial = await this.generateKeyMaterial(password);
        const key = await this.generateKey(keyMaterial, salt)

        const decryptedData = await crypto.subtle.decrypt(
            {
                name: algorithm,
                iv
            },
            key,
            ciphertext
        );

        return this.textDecoder.decode(decryptedData);
    }

    async testAesGcm() {
        const plaintext = 'Hello World';
        const password = 'password_is_password';

        const encryptedData = await this.encrypt(plaintext, password);
        console.log(encryptedData);

        const decryptedData = await this.decrypt(encryptedData, password);
        console.log(decryptedData);
    }
}

const aesGcm = new AESGCM();
aesGcm.testAesGcm();

Typecript - Javascript's Safety Net ๐Ÿ›ก๏ธ

class AESGCM {
    static ALGORITHM: string = "AES-GCM";
    static HASH: string = "SHA-256";
    static KEY_SIZE: number = 256;
    static ITERATION_COUNT: number = 100000;

    private textEncoder: TextEncoder;
    private textDecoder: TextDecoder;

    constructor() {
        this.textEncoder = new TextEncoder();
        this.textDecoder = new TextDecoder();
    }

    async generateKeyMaterial(password: string): Promise<CryptoKey> {
        return crypto.subtle.importKey(
            "raw",
            this.textEncoder.encode(password),
            { name: "PBKDF2" },
            false,
            ["deriveBits", "deriveKey"]
        );
    }

    async generateKey(keyMaterial: CryptoKey, salt: Uint8Array): Promise<CryptoKey> {
        const algorithm: string = AESGCM.ALGORITHM;
        const hash: string = AESGCM.HASH;
        const iterationCount: number = AESGCM.ITERATION_COUNT;
        const keySize: number = AESGCM.KEY_SIZE;

        return crypto.subtle.deriveKey(
            {
                name: "PBKDF2",
                salt,
                iterations: iterationCount,
                hash: hash
            },
            keyMaterial,
            { 
                name: algorithm,
                length: keySize 
            },
            true,
            ["encrypt", "decrypt"]
        );
    }

    async encrypt(plaintext: string, password: string): Promise<string> {
        const data: Uint8Array = this.textEncoder.encode(plaintext);
        const salt: Uint8Array = crypto.getRandomValues(new Uint8Array(16));
        const keyMaterial: CryptoKey = await this.generateKeyMaterial(password);
        const key: CryptoKey = await this.generateKey(keyMaterial, salt);
        const algorithm: string = AESGCM.ALGORITHM;
        const iv: Uint8Array = crypto.getRandomValues(new Uint8Array(12));
        const encryptedData: ArrayBuffer = await crypto.subtle.encrypt(
            {
                name: algorithm,
                iv
            },
            key,
            data
        );

        const ciphertextBase64: string = btoa(String.fromCharCode.apply(null, new Uint8Array(encryptedData)));
        const ivBase64: string = btoa(String.fromCharCode.apply(null, iv));
        const saltBase64: string = btoa(String.fromCharCode.apply(null, salt));

        return ciphertextBase64 + ":" + ivBase64 + ":" + saltBase64;
    }

    async decrypt(encryptedData: string, password: string): Promise<string> {
        const [ciphertextStr, ivStr, saltStr]: string[] = encryptedData.split(":");
        const ciphertext: Uint8Array = Uint8Array.from(atob(ciphertextStr), c => c.charCodeAt(0));
        const iv: Uint8Array = Uint8Array.from(atob(ivStr), c => c.charCodeAt(0));
        const salt: Uint8Array = Uint8Array.from(atob(saltStr), c => c.charCodeAt(0));
        const algorithm: string = AESGCM.ALGORITHM;
        const keyMaterial: CryptoKey = await this.generateKeyMaterial(password);
        const key: CryptoKey = await this.generateKey(keyMaterial, salt)

        const decryptedData: ArrayBuffer = await crypto.subtle.decrypt(
            {
                name: algorithm,
                iv
            },
            key,
            ciphertext
        );

        return this.textDecoder.decode(decryptedData);
    }

    async testAesGcm(): Promise<void> {
        const plaintext: string = 'Hello World';
        const password: string = 'password_is_password';

        const encryptedData: string = await this.encrypt(plaintext, password);
        console.log(encryptedData);

        const decryptedData: string = await this.decrypt(encryptedData, password);
        console.log(decryptedData);
    }
}

const aesGcm: AESGCM = new AESGCM();
aesGcm.testAesGcm();

Java - Sip into Secure Coding โ˜•

package com.crypto.crypto;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.security.spec.KeySpec;
import java.util.Base64;

public class AESGCM {
    private static final String ALGORITHM = "AES/GCM/NoPadding";
    private static final int KEY_SIZE = 256;
    private static final int ITERATION_COUNT = 100000;
    private static final int TAG_LENGTH = 128;

    public static String encrypt(String plaintext, String password) throws Exception {
        byte[] salt = generateRandomBytes(16);
        SecretKey keySpec = deriveKeyFromPassword(password, salt);

        Cipher cipher = Cipher.getInstance(ALGORITHM);
        byte[] iv = generateRandomBytes(12);
        GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(TAG_LENGTH, iv);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec);

        byte[] ciphertext = cipher.doFinal(plaintext.getBytes());
        return Base64.getEncoder().encodeToString(ciphertext) + ":" +
                Base64.getEncoder().encodeToString(iv) + ":" +
                Base64.getEncoder().encodeToString(salt);
    }

    public static String decrypt(String ciphertext, String password) throws Exception {
        String[] parts = ciphertext.split(":");
        byte[] encryptedData = Base64.getDecoder().decode(parts[0]);
        byte[] iv = Base64.getDecoder().decode(parts[1]);
        byte[] salt = Base64.getDecoder().decode(parts[2]);

        SecretKey keySpec = deriveKeyFromPassword(password, salt);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(TAG_LENGTH, iv);
        cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);

        byte[] decryptedData = cipher.doFinal(encryptedData);
        return new String(decryptedData);
    }

    private static SecretKey deriveKeyFromPassword(String password, byte[] salt) throws Exception {
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATION_COUNT, KEY_SIZE);
        SecretKey tmp = factory.generateSecret(spec);
        return new SecretKeySpec(tmp.getEncoded(), "AES");
    }

    private static byte[] generateRandomBytes(int length) {
        SecureRandom secureRandom = new SecureRandom();
        byte[] bytes = new byte[length];
        secureRandom.nextBytes(bytes);
        return bytes;
    }

    public static void main(String[] args) throws Exception {
        String plaintext = "Hello World";
        String password = "password_is_password";

        String encryptedData = encrypt(plaintext, password);
        System.out.println(encryptedData);

        String decryptedData = decrypt(encryptedData, password);
        System.out.println(decryptedData);
    }
}

Python - The Easy-to-Read Language ๐Ÿ

import os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
import base64

ALGORITHM = "AES"
KEY_SIZE = 32  # 256 bits
ITERATION_COUNT = 100000
TAG_LENGTH = 16  # 128 bits

def encrypt(plaintext, password):
    salt = os.urandom(16)
    key = derive_key_from_password(password, salt)

    aesgcm = AESGCM(key)
    nonce = os.urandom(12)
    ciphertext = aesgcm.encrypt(nonce, plaintext.encode(), None)

    encoded_ciphertext = base64.b64encode(ciphertext).decode('utf-8')
    encoded_nonce = base64.b64encode(nonce).decode('utf-8')
    encoded_salt = base64.b64encode(salt).decode('utf-8')

    return f"{encoded_ciphertext}:{encoded_nonce}:{encoded_salt}"

def decrypt(ciphertext, password):
    parts = ciphertext.split(':')
    encoded_ciphertext, encoded_nonce, encoded_salt = parts

    ciphertext = base64.b64decode(encoded_ciphertext)
    nonce = base64.b64decode(encoded_nonce)
    salt = base64.b64decode(encoded_salt)

    key = derive_key_from_password(password, salt)

    aesgcm = AESGCM(key)
    plaintext = aesgcm.decrypt(nonce, ciphertext, None)

    return plaintext.decode('utf-8')

def derive_key_from_password(password, salt):
    kdf = PBKDF2HMAC(
        algorithm=hashes.SHA256(),
        length=KEY_SIZE,
        salt=salt,
        iterations=ITERATION_COUNT,
        backend=default_backend()
    )
    return kdf.derive(password.encode())

if __name__ == "__main__":
    plaintext = "Hello World"
    password = "password_is_password"

    encrypted_data = encrypt(plaintext, password)
    print(encrypted_data)

    decrypted_data = decrypt(encrypted_data, password)
    print(decrypted_data)

Conclusion ๐ŸŽ‰

We've learned how to securely encrypt and decrypt data in JavaScript, TypeScript, Java, and Python. By using AES-GCM, your sensitive information stays confidential and protected. And remember, besides the languages we've covered, AES-GCM can be applied in PHP, Go, Rust, and more.

Remember, the security of your encrypted data hinges on the strength of your encryption key (password). So, always keep your password secure and avoid sharing it with unauthorized individuals. With AES-GCM, your data remains safe from unauthorized access.

Keep coding, keep exploring, and make data security a top priority in your applications! ๐Ÿ”๐Ÿš€


That's it ๐Ÿ˜

Thank you for reading! If you found this article helpful, please share your thoughts in the comments. Additionally, if you noticed any mistakes, sharing them will help me improve. Your feedback is greatly appreciated!

And Don't forget to show your love with "๐Ÿ’– * 10" by giving ten hearts if you enjoyed it on Hashnode! Your support is truly heartwarming! ๐Ÿš€โœจ

"Thank you"

ย