DEV Community

Cover image for Secure Your Data: AES-GCM Encryption & Decryption for JavaScript, TypeScript, Java, and Python 🛡️🔐
Mohamed Ismail
Mohamed Ismail

Posted on • Edited on

Secure Your Data: AES-GCM Encryption & Decryption for JavaScript, TypeScript, Java, and Python 🛡️🔐

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);
}
Enter fullscreen mode Exit fullscreen mode

}

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

---
### Typecript - Javascript's Safety Net 🛡️
>
```ts
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();

Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

}

---
### Python -   The Easy-to-Read Language 🐍
> 
```python
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)
Enter fullscreen mode Exit fullscreen mode

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 forgot to give a "💖 🦄 🤯 🙌 🔥" if you enjoyed it!

Top comments (0)