Python AES-GCM Code Example (Online Runner)

Python AES-GCM examples with tag length, AAD, and encoding controls matching the AES-GCM tool.

Online calculator: use the site AES-GCM tool.

Note: This snippet requires locally installed dependencies and will not run in the online runner.

Calculation method

AES-GCM combines encryption and authentication. The tool lets you set key size, nonce, AAD, tag length, and encoding for ciphertext + tag. The helper below mirrors those fields using PyCryptodome.

Install the dependency first: pip install pycryptodome.

Implementation notes

  • Package: pycryptodome provides Crypto.Cipher.AES with GCM mode.
  • Implementation: the snippet concatenates ciphertext + tag and encodes them as hex/base64 to match the UI output. The tag length is in bytes (typically 16).
  • Notes: nonces must be unique per key (12 bytes is standard). AAD must match during decryption or verification fails and raises an exception.
python
from __future__ import annotations

import base64
from typing import Literal

from Crypto.Cipher import AES

KeyEncoding = Literal["utf8", "hex"]
CipherEncoding = Literal["hex", "base64"]


def _decode_value(value: str, encoding: KeyEncoding) -> bytes:
    return value.encode("utf-8") if encoding == "utf8" else bytes.fromhex(value)


def _encode_ciphertext(data: bytes, encoding: CipherEncoding) -> str:
    return data.hex() if encoding == "hex" else base64.b64encode(data).decode("ascii")


def _decode_ciphertext(value: str, encoding: CipherEncoding) -> bytes:
    return bytes.fromhex(value) if encoding == "hex" else base64.b64decode(value)


def _normalize_key(key: bytes, key_size: int) -> bytes:
    return key[:key_size].ljust(key_size, b"\x00")


def aes_gcm_encrypt(
    plaintext: str,
    key: str,
    nonce: str,
    *,
    key_encoding: KeyEncoding = "hex",
    nonce_encoding: KeyEncoding = "hex",
    aad: str | None = None,
    aad_encoding: KeyEncoding = "utf8",
    tag_length: int = 16,
    key_size: int = 32,
    output_encoding: CipherEncoding = "base64",
) -> str:
    key_bytes = _normalize_key(_decode_value(key, key_encoding), key_size)
    nonce_bytes = _decode_value(nonce, nonce_encoding)
    cipher = AES.new(key_bytes, AES.MODE_GCM, nonce=nonce_bytes, mac_len=tag_length)
    if aad:
        cipher.update(_decode_value(aad, aad_encoding))
    ciphertext, tag = cipher.encrypt_and_digest(plaintext.encode("utf-8"))
    combined = ciphertext + tag
    return _encode_ciphertext(combined, output_encoding)


def aes_gcm_decrypt(
    combined: str,
    key: str,
    nonce: str,
    *,
    key_encoding: KeyEncoding = "hex",
    nonce_encoding: KeyEncoding = "hex",
    aad: str | None = None,
    aad_encoding: KeyEncoding = "utf8",
    tag_length: int = 16,
    key_size: int = 32,
    input_encoding: CipherEncoding = "base64",
) -> str:
    key_bytes = _normalize_key(_decode_value(key, key_encoding), key_size)
    nonce_bytes = _decode_value(nonce, nonce_encoding)
    data = _decode_ciphertext(combined, input_encoding)
    if len(data) < tag_length:
        raise ValueError("Ciphertext must include the authentication tag")
    ciphertext, tag = data[:-tag_length], data[-tag_length:]
    cipher = AES.new(key_bytes, AES.MODE_GCM, nonce=nonce_bytes, mac_len=tag_length)
    if aad:
        cipher.update(_decode_value(aad, aad_encoding))
    plaintext = cipher.decrypt_and_verify(ciphertext, tag)
    return plaintext.decode("utf-8", errors="replace")

# Example usage
key = "00112233445566778899aabbccddeeff"
nonce = "0102030405060708090a0b0c"

payload = aes_gcm_encrypt(
    "hello",
    key,
    nonce,
    key_encoding="hex",
    nonce_encoding="hex",
    aad="context",
    aad_encoding="utf8",
    tag_length=16,
    key_size=16,
    output_encoding="hex",
)
print(payload)

plain = aes_gcm_decrypt(
    payload,
    key,
    nonce,
    key_encoding="hex",
    nonce_encoding="hex",
    aad="context",
    aad_encoding="utf8",
    tag_length=16,
    key_size=16,
    input_encoding="hex",
)
print(plain)

Complete script (implementation + tests)

python
from __future__ import annotations

import base64
from typing import Literal

from Crypto.Cipher import AES

KeyEncoding = Literal["utf8", "hex"]
CipherEncoding = Literal["hex", "base64"]


def _decode_value(value: str, encoding: KeyEncoding) -> bytes:
    return value.encode("utf-8") if encoding == "utf8" else bytes.fromhex(value)


def _encode_ciphertext(data: bytes, encoding: CipherEncoding) -> str:
    return data.hex() if encoding == "hex" else base64.b64encode(data).decode("ascii")


def _decode_ciphertext(value: str, encoding: CipherEncoding) -> bytes:
    return bytes.fromhex(value) if encoding == "hex" else base64.b64decode(value)


def _normalize_key(key: bytes, key_size: int) -> bytes:
    return key[:key_size].ljust(key_size, b"\x00")


def aes_gcm_encrypt(
    plaintext: str,
    key: str,
    nonce: str,
    *,
    key_encoding: KeyEncoding = "hex",
    nonce_encoding: KeyEncoding = "hex",
    aad: str | None = None,
    aad_encoding: KeyEncoding = "utf8",
    tag_length: int = 16,
    key_size: int = 32,
    output_encoding: CipherEncoding = "base64",
) -> str:
    key_bytes = _normalize_key(_decode_value(key, key_encoding), key_size)
    nonce_bytes = _decode_value(nonce, nonce_encoding)
    cipher = AES.new(key_bytes, AES.MODE_GCM, nonce=nonce_bytes, mac_len=tag_length)
    if aad:
        cipher.update(_decode_value(aad, aad_encoding))
    ciphertext, tag = cipher.encrypt_and_digest(plaintext.encode("utf-8"))
    combined = ciphertext + tag
    return _encode_ciphertext(combined, output_encoding)


def aes_gcm_decrypt(
    combined: str,
    key: str,
    nonce: str,
    *,
    key_encoding: KeyEncoding = "hex",
    nonce_encoding: KeyEncoding = "hex",
    aad: str | None = None,
    aad_encoding: KeyEncoding = "utf8",
    tag_length: int = 16,
    key_size: int = 32,
    input_encoding: CipherEncoding = "base64",
) -> str:
    key_bytes = _normalize_key(_decode_value(key, key_encoding), key_size)
    nonce_bytes = _decode_value(nonce, nonce_encoding)
    data = _decode_ciphertext(combined, input_encoding)
    if len(data) < tag_length:
        raise ValueError("Ciphertext must include the authentication tag")
    ciphertext, tag = data[:-tag_length], data[-tag_length:]
    cipher = AES.new(key_bytes, AES.MODE_GCM, nonce=nonce_bytes, mac_len=tag_length)
    if aad:
        cipher.update(_decode_value(aad, aad_encoding))
    plaintext = cipher.decrypt_and_verify(ciphertext, tag)
    return plaintext.decode("utf-8", errors="replace")


def run_tests() -> None:
    key = "00112233445566778899aabbccddeeff"
    nonce = "0102030405060708090a0b0c"
    combined = aes_gcm_encrypt("hello", key, nonce, key_encoding="hex", nonce_encoding="hex", output_encoding="hex", key_size=16)
    recovered = aes_gcm_decrypt(combined, key, nonce, key_encoding="hex", nonce_encoding="hex", input_encoding="hex", key_size=16)
    assert recovered == "hello"
    print("AES-GCM tests passed")


if __name__ == "__main__":
    run_tests()