Skip to content
endec

Encryption code examples

Encrypting and decrypting "Hello, World!" with AES-256-GCM. Every example writes the same format: a random 12-byte nonce, then the ciphertext, then the 16-byte tag, all in Base64. So any of them can decrypt the others' output, and so can this site: choose Decrypt, AES-256, GCM, Key, paste the key below, and turn on The nonce is at the start.

Demo key (hex): 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f

In real code, generate a random key once, keep it out of your source code (an environment variable or a secrets manager), and never reuse a nonce with the same key.

Encryption in JavaScript (browser)

Web Crypto's AES-GCM appends the 16-byte tag to the ciphertext, so nonce + result is the whole message.

const hex = (s) => Uint8Array.from(s.match(/../g), (h) => parseInt(h, 16));
const toB64 = (b) => btoa(String.fromCharCode(...b));
const fromB64 = (s) => Uint8Array.from(atob(s), (c) => c.charCodeAt(0));

const key = await crypto.subtle.importKey(
  "raw", hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"), "AES-GCM", false, ["encrypt", "decrypt"]
);

// Encrypt: random 12-byte nonce, then nonce + ciphertext + tag as Base64
const nonce = crypto.getRandomValues(new Uint8Array(12));
const ct = new Uint8Array(
  await crypto.subtle.encrypt({ name: "AES-GCM", iv: nonce }, key, new TextEncoder().encode("Hello, World!"))
);
const message = toB64(new Uint8Array([...nonce, ...ct]));
console.log(message);

// Decrypt (throws if the key is wrong or the data was changed)
const data = fromB64(message);
const pt = await crypto.subtle.decrypt({ name: "AES-GCM", iv: data.slice(0, 12) }, key, data.slice(12));
console.log(new TextDecoder().decode(pt));

Encryption in Node.js

import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";

const key = Buffer.from("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "hex");

// Encrypt
const nonce = randomBytes(12);
const cipher = createCipheriv("aes-256-gcm", key, nonce);
const ct = Buffer.concat([cipher.update("Hello, World!", "utf8"), cipher.final()]);
const message = Buffer.concat([nonce, ct, cipher.getAuthTag()]).toString("base64");
console.log(message);

// Decrypt
const data = Buffer.from(message, "base64");
const decipher = createDecipheriv("aes-256-gcm", key, data.subarray(0, 12));
decipher.setAuthTag(data.subarray(-16));
const pt = Buffer.concat([decipher.update(data.subarray(12, -16)), decipher.final()]);
console.log(pt.toString("utf8"));

Encryption in Python

Install: pip install cryptography

import base64
import os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM

key = bytes.fromhex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
aes = AESGCM(key)

# Encrypt (the tag is appended to the ciphertext)
nonce = os.urandom(12)
message = base64.b64encode(nonce + aes.encrypt(nonce, b"Hello, World!", None)).decode()
print(message)

# Decrypt (raises InvalidTag if the key is wrong or the data was changed)
data = base64.b64decode(message)
print(aes.decrypt(data[:12], data[12:], None).decode())

Encryption in Java

import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.HexFormat;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class Main {
  public static void main(String[] args) throws Exception {
    SecretKeySpec key = new SecretKeySpec(HexFormat.of().parseHex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"), "AES");

    // Encrypt (Java appends the tag to the ciphertext)
    byte[] nonce = new byte[12];
    new SecureRandom().nextBytes(nonce);
    Cipher enc = Cipher.getInstance("AES/GCM/NoPadding");
    enc.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(128, nonce));
    byte[] ct = enc.doFinal("Hello, World!".getBytes(StandardCharsets.UTF_8));
    byte[] all = new byte[nonce.length + ct.length];
    System.arraycopy(nonce, 0, all, 0, 12);
    System.arraycopy(ct, 0, all, 12, ct.length);
    String message = Base64.getEncoder().encodeToString(all);
    System.out.println(message);

    // Decrypt (throws AEADBadTagException if the key is wrong or the data was changed)
    byte[] data = Base64.getDecoder().decode(message);
    Cipher dec = Cipher.getInstance("AES/GCM/NoPadding");
    dec.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(128, data, 0, 12));
    System.out.println(new String(dec.doFinal(data, 12, data.length - 12), StandardCharsets.UTF_8));
  }
}

Encryption in C# / .NET

AesGcm needs .NET Core 3.0 or later. It keeps the tag separate, so it's written after the ciphertext by hand.

using System;
using System.Security.Cryptography;
using System.Text;

byte[] key = Convert.FromHexString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
using var aes = new AesGcm(key, tagSizeInBytes: 16);

// Encrypt: nonce + ciphertext + tag
byte[] pt = Encoding.UTF8.GetBytes("Hello, World!");
byte[] all = new byte[12 + pt.Length + 16];
var nonce = all.AsSpan(0, 12);
RandomNumberGenerator.Fill(nonce);
aes.Encrypt(nonce, pt, all.AsSpan(12, pt.Length), all.AsSpan(12 + pt.Length));
string message = Convert.ToBase64String(all);
Console.WriteLine(message);

// Decrypt (throws AuthenticationTagMismatchException if the key is wrong or the data was changed)
byte[] data = Convert.FromBase64String(message);
byte[] plain = new byte[data.Length - 28];
aes.Decrypt(data.AsSpan(0, 12), data.AsSpan(12, plain.Length), data.AsSpan(data.Length - 16), plain);
Console.WriteLine(Encoding.UTF8.GetString(plain));

Encryption in Go

package main

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/base64"
	"encoding/hex"
	"fmt"
)

func main() {
	key, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}
	gcm, err := cipher.NewGCM(block)
	if err != nil {
		panic(err)
	}

	// Encrypt: Seal appends ciphertext + tag to the nonce
	nonce := make([]byte, gcm.NonceSize())
	rand.Read(nonce)
	message := base64.StdEncoding.EncodeToString(gcm.Seal(nonce, nonce, []byte("Hello, World!"), nil))
	fmt.Println(message)

	// Decrypt (errors if the key is wrong or the data was changed)
	data, _ := base64.StdEncoding.DecodeString(message)
	pt, err := gcm.Open(nil, data[:12], data[12:], nil)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(pt))
}

Encryption in Rust

Install: cargo add aes-gcm@0.10 base64 hex

These calls are from aes-gcm 0.10, the widely used release; 0.11 changed how nonces are made.

use aes_gcm::aead::{Aead, AeadCore, KeyInit, OsRng};
use aes_gcm::{Aes256Gcm, Key, Nonce};
use base64::{engine::general_purpose::STANDARD, Engine};

fn main() {
    let key_bytes = hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap();
    let cipher = Aes256Gcm::new(Key::<Aes256Gcm>::from_slice(&key_bytes));

    // Encrypt: nonce + ciphertext + tag
    let nonce = Aes256Gcm::generate_nonce(&mut OsRng);
    let ct = cipher.encrypt(&nonce, b"Hello, World!".as_ref()).unwrap();
    let message = STANDARD.encode([nonce.as_slice(), &ct].concat());
    println!("{message}");

    // Decrypt (errors if the key is wrong or the data was changed)
    let data = STANDARD.decode(&message).unwrap();
    let pt = cipher.decrypt(Nonce::from_slice(&data[..12]), &data[12..]).unwrap();
    println!("{}", String::from_utf8(pt).unwrap());
}

Encryption in PHP

<?php
$key = hex2bin("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");

// Encrypt
$nonce = random_bytes(12);
$ct = openssl_encrypt("Hello, World!", "aes-256-gcm", $key, OPENSSL_RAW_DATA, $nonce, $tag);
$message = base64_encode($nonce . $ct . $tag);
echo $message, "\n";

// Decrypt (returns false if the key is wrong or the data was changed)
$data = base64_decode($message);
$pt = openssl_decrypt(substr($data, 12, -16), "aes-256-gcm", $key, OPENSSL_RAW_DATA, substr($data, 0, 12), substr($data, -16));
echo $pt, "\n";

Encryption in OpenSSL (command line)

openssl enc can't do GCM, so this uses AES-256-CBC with a password. Decrypt the output here with Password, PBKDF2-SHA256 and 600000 iterations.

# Encrypt with a password (prompts for it), Base64 output on one line
echo -n 'Hello, World!' | openssl enc -aes-256-cbc -pbkdf2 -iter 600000 -a -A > message.txt

# Decrypt
openssl enc -d -aes-256-cbc -pbkdf2 -iter 600000 -a -A -in message.txt