A working example, across three runtimes
Web Crypto AES-GCM + PBKDF2: encrypt and decrypt
Encrypt a message in your browser, download its JSON, and recover the same text in Node.js or Python. Run the example here or download the complete source.
Reviewed September 16, 2026 · Browser, Node.js and Python interoperability checks
1. Run it in your browser
One click encrypts and decrypts the sample, then checks the published test vector. The cryptographic work stays on your device.
Message in → same message out
HashyTools AES-GCM v2 test vector
Ready to try. A fresh salt and IV are generated each time.
Public demo password: correct horse battery staple. These examples are for test data. Keep your own secrets out of this demo.
2. Recover the same text in another runtime
Save payload.json from step 1 and your chosen script in the same folder. The scripts use the public demo password and print the recovered message.
Node.js — no packages needed
Requires Node.js 20 or later. Download the script, open a terminal in the download folder, then run:
Download Node.js scriptnode decrypt.mjs payload.json
It derives the key from the payload parameters, sets AAD and the authentication tag, and prints plaintext only after authentication succeeds.
Python — install cryptography once
Requires Python 3.10 or later. Download the script, open a terminal in the download folder, install the dependency and run:
Download Python scriptpython -m pip install cryptography
python decrypt.py payload.json
On systems where Python is named python3, use that command instead. Python joins ciphertext and tag before calling AESGCM.decrypt().
Browser — run the complete example locally
Download both files into one folder. The module includes key derivation, Base64URL conversion, encryption and decryption. No JavaScript packages are required.
Serve that folder with Python, then open http://127.0.0.1:8000/demo.htm in a browser:
python -m http.server 8000 --bind 127.0.0.1
Use localhost or HTTPS for Web Crypto. Double-clicking an HTML file is not a reliable way to load JavaScript modules. Stop the local server with Ctrl+C when finished.
Expected output from either script: HashyTools AES-GCM v2 test vector.
3. Verify against a fixed test vector
Download the published vector into the same folder. Unlike the fresh browser payload, its salt, IV and expected ciphertext are fixed so results can be checked independently.
Download vector JSONnode decrypt.mjs aes-gcm-v2-test-vector.json
python decrypt.py aes-gcm-v2-test-vector.json
Each script checks the expected plaintext and exits with an error on a mismatch. Fixed values and the public password are only for tests. New encryption must use fresh randomness and your own secret.
Read the reproducible test vectorsWhat travels in the JSON?
This is the HashyTools AES v2 contract, not a universal AES file format. It records PBKDF2-SHA-256 parameters, a 16-byte salt, 12-byte IV, ciphertext, a separate 16-byte tag and optional AAD. Binary fields use unpadded Base64URL. The password is never included.
New examples derive a 256-bit key with 600,000 iterations. Readers use the value stored in the payload, with a supported range of 10,000–2,000,000. AAD is authenticated but remains visible.
If decryption fails
Invalid Base64URL or unexpected byte lengths
Decode the JSON fields as Base64URL, not as hex or literal UTF-8 bytes. This format uses - and _ without = padding. The scripts add padding where required. Check that the decoded salt is 16 bytes, IV is 12 and tag is 16. A truncated tag or an invalid character is rejected before key derivation.
OperationError, InvalidTag or authentication failure
Check the password, salt, IV, ciphertext, tag, AAD and PBKDF2 settings together. Authentication failure does not identify which value is wrong. Use the original AAD bytes: even a changed character makes verification fail. Do not bypass the tag check or display unauthenticated plaintext.
The tag works in one runtime but not another
Web Crypto and Python AESGCM expect ciphertext + tag. Node.js expects the ciphertext alone in update() and the tag separately in setAuthTag() before final(). The JSON stores them separately; join them exactly once where needed.
Different PBKDF2 key or unsupported parameters
Use PBKDF2-HMAC-SHA-256, UTF-8 password bytes, the decoded salt and the payload's iteration count. Key length is 256 bits in Web Crypto and 32 bytes in Node.js and Python. These examples reject other formats and out-of-range work factors. Changing an in-range iteration count changes the key and causes authentication to fail.
Bring AES-GCM parameters from another application
If your application already has a 32-byte AES key, use Key and separate parameters in the AES tool. This mode uses a 12-byte IV and 16-byte tag. It does not derive a key from a password or require HashyTools JSON v2.
The following scripts use a public demonstration key only. Never use it to protect real data. Each new encryption generates a random IV. No input or key is uploaded.
- Save the scripts in one folder. Generate parameters with Node.js 20+:
node raw-node.mjs encrypt node-payload.json. - Open the AES tool, select Key and separate parameters, then Decrypt. Paste the public key below. Copy IV, ciphertext, tag and AAD from
node-payload.json, selecting Hex for each field and Separate tag. - Decrypt. Expected text:
Hello from Node.js — Hola, niño 🔐. - Switch to Encrypt, enter any test message and keep the public key. Encrypt, choose Hex and Separate tag, then download
aes-gcm-parameters.json. Its key is deliberately omitted. - With Python 3.10+ and
python -m pip install cryptography, runpython raw-python.py decrypt aes-gcm-parameters.json. The recovered text must match your message.
Public test key (Hex):
000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
Try the other direction or check authentication
Run python raw-python.py encrypt python-payload.json and decrypt its parameters in the browser, or run node raw-node.mjs decrypt python-payload.json. Change one byte of the tag to confirm that authentication fails. Neither script releases plaintext before authentication. Both scripts also accept Base64/Base64URL exports and a tag appended to ciphertext.
Method and sources
Verification covers the published vector and fresh browser payloads in Chromium, Node.js and Python, including rejection of altered AAD, tags and derivation parameters. These checks demonstrate interoperability; they are not an independent security audit.
Browse other tools
Choose a developer tool
- AES-GCM encrypt/decrypt
Protect text with AES-GCM and PBKDF2 tokens that stay on-device.
- Hash generator
Generate MD5, SHA-1, and SHA-256 digests locally with Web Workers.
- JWT verifier
Decode claims and verify HMAC signatures without network calls.
- Base64 encoder & decoder
Convert any text to or from Base64 without leaving your browser.
More tools for data, text and testing
- JSON formatter
Validate, format, and diff JSON without network calls.
- XML formatter
Format, minify and convert XML to JSON locally.
- Regex tester
Experiment with regular expressions and capture groups locally.
- URL encoder and inspector
Encode components and inspect query parameters locally.
- UUID v4 generator
Create collision-resistant UUID v4 identifiers using Web Crypto.
- Secure password generator
Build strong passwords with custom length, charset, and entropy.
- Epoch time converter
Convert Unix timestamps into local, UTC and ISO dates.
- Text transformer
Convert case, slugify, and clean strings on-device.
- Color converter
Switch between HEX, RGB, HSL, and Tailwind palettes instantly.
- QR code builder
Design QR codes for text, Wi-Fi, contacts, email, or SMS locally.
- Barcode maker
Generate EAN and Code 128 barcodes and download crisp PNG output.
- IP lookup
Look up your public IP using disclosed external services.
- Lorem ipsum generator
Produce paragraphs, lists, and markdown filler text fast.
Run the AES interoperability guide or read how these tools are checked.