Reference implementation
SubtleCrypto PBKDF2 and AES-GCM example
This guide shows how to call SubtleCrypto.deriveKey() with PBKDF2-SHA-256 and use the derived key with AES-256-GCM. The same explicit payload can be reproduced in a browser, Node.js and Python.
Browser Web Crypto example
All byte fields are Base64URL. AES-GCM output is split into ciphertext and its final 16-byte authentication tag. HashyTools records PBKDF2-SHA-256 and 600,000 iterations in each new payload instead of relying on hidden defaults.
const bytes = new TextEncoder().encode(plaintext);
const keyMaterial = await crypto.subtle.importKey('raw', new TextEncoder().encode(password), 'PBKDF2', false, ['deriveKey']);
const key = await crypto.subtle.deriveKey(
{ name: 'PBKDF2', hash: 'SHA-256', salt, iterations: 600000 }, keyMaterial,
{ name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt']
);
const encrypted = new Uint8Array(await crypto.subtle.encrypt({ name: 'AES-GCM', iv, additionalData: aad }, key, bytes));
const ciphertext = encrypted.slice(0, -16);
const tag = encrypted.slice(-16);Node.js compatibility
import { createDecipheriv, pbkdf2Sync } from 'node:crypto';
const key = pbkdf2Sync(password, salt, 600000, 32, 'sha256');
const decipher = createDecipheriv('aes-256-gcm', key, iv);
decipher.setAAD(aad);
decipher.setAuthTag(tag);
const plaintext = Buffer.concat([
decipher.update(ciphertext),
decipher.final()
]).toString('utf8');Decode each Base64URL field before passing it to Node. The authentication tag must be set before final().
Python compatibility
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
key = PBKDF2HMAC(hashes.SHA256(), 32, salt, 600000).derive(password.encode())
encrypted = AESGCM(key).encrypt(iv, plaintext.encode(), aad)
ciphertext, tag = encrypted[:-16], encrypted[-16:]Encode salt, iv, ciphertext, tag and optional aad as Base64URL before writing the JSON fields.
Test before production
Use a fixed password, salt, IV and AAD only in an automated test, then compare the decoded ciphertext and tag byte-for-byte between runtimes. Generate fresh random salt and IV values for actual encryption.
Method and sources
Written and tested by the HashyTools engineering team. Reviewed August 31, 2026. Examples are checked against the published deterministic vector and automated browser tests.