Python AES Encryption Code Example (Online Runner)
Python AES examples with mode, padding, key size, and encoding controls to mirror the AES tool.
Online calculator: use the site AES tool.
Note: This snippet requires locally installed dependencies and will not run in the online runner.
Calculation method
The AES tool supports ECB/CBC/CFB/OFB/CTR modes, PKCS7 or zero padding (for block modes), key size selection, and hex or base64 ciphertext encodings. This helper mirrors those options using PyCryptodome.
Install the dependency first: pip install pycryptodome.
Implementation notes
- Package:
pycryptodomeprovidesCrypto.Cipher.AESandCrypto.Util.Counter. - Implementation: keys are normalized to the selected byte length (16/24/32) to match the tool, and IVs are padded or truncated to 16 bytes. CTR builds its counter from the IV bytes so encryption and decryption must reuse the same IV.
- Notes: padding only applies to ECB/CBC. Zero padding is ambiguous if plaintext ends with
\x00; prefer PKCS7 unless you control both ends. For real usage generate a random IV per message and store it alongside the ciphertext.
python
from __future__ import annotations
import base64
from typing import Literal
from Crypto.Cipher import AES
from Crypto.Util import Counter
Mode = Literal["ECB", "CBC", "CFB", "OFB", "CTR"]
Padding = Literal["PKCS7", "Zero"]
KeyEncoding = Literal["utf8", "hex"]
CipherEncoding = Literal["hex", "base64"]
BLOCK_SIZE = 16
def _normalize_key(key: bytes, key_size: int) -> bytes:
padded = key[:key_size].ljust(key_size, b"\x00")
return padded
def _normalize_iv(iv: bytes | None) -> bytes:
return (iv or b"")[:BLOCK_SIZE].ljust(BLOCK_SIZE, b"\x00")
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 _pkcs7_pad(data: bytes) -> bytes:
pad_len = BLOCK_SIZE - (len(data) % BLOCK_SIZE)
return data + bytes([pad_len] * pad_len)
def _pkcs7_unpad(data: bytes) -> bytes:
if not data:
return data
pad_len = data[-1]
if pad_len < 1 or pad_len > BLOCK_SIZE:
raise ValueError("Invalid PKCS7 padding")
if data[-pad_len:] != bytes([pad_len] * pad_len):
raise ValueError("Invalid PKCS7 padding")
return data[:-pad_len]
def _zero_pad(data: bytes) -> bytes:
if len(data) % BLOCK_SIZE == 0:
return data
pad_len = BLOCK_SIZE - (len(data) % BLOCK_SIZE)
return data + b"\x00" * pad_len
def _zero_unpad(data: bytes) -> bytes:
return data.rstrip(b"\x00")
def _build_cipher(key: bytes, mode: Mode, iv: bytes | None) -> AES:
if mode == "ECB":
return AES.new(key, AES.MODE_ECB)
if mode == "CBC":
return AES.new(key, AES.MODE_CBC, iv=_normalize_iv(iv))
if mode == "CFB":
return AES.new(key, AES.MODE_CFB, iv=_normalize_iv(iv), segment_size=128)
if mode == "OFB":
return AES.new(key, AES.MODE_OFB, iv=_normalize_iv(iv))
counter = Counter.new(128, initial_value=int.from_bytes(_normalize_iv(iv), "big"))
return AES.new(key, AES.MODE_CTR, counter=counter)
def aes_encrypt(
plaintext: str,
key: str,
*,
mode: Mode = "CBC",
padding: Padding = "PKCS7",
key_encoding: KeyEncoding = "hex",
iv: str | None = None,
iv_encoding: KeyEncoding = "hex",
key_size: int = 32,
output_encoding: CipherEncoding = "base64",
) -> str:
key_bytes = _normalize_key(_decode_value(key, key_encoding), key_size)
iv_bytes = _decode_value(iv, iv_encoding) if iv is not None else None
data = plaintext.encode("utf-8")
if mode in {"ECB", "CBC"}:
data = _pkcs7_pad(data) if padding == "PKCS7" else _zero_pad(data)
cipher = _build_cipher(key_bytes, mode, iv_bytes)
encrypted = cipher.encrypt(data)
return _encode_ciphertext(encrypted, output_encoding)
def aes_decrypt(
ciphertext: str,
key: str,
*,
mode: Mode = "CBC",
padding: Padding = "PKCS7",
key_encoding: KeyEncoding = "hex",
iv: str | None = None,
iv_encoding: KeyEncoding = "hex",
key_size: int = 32,
input_encoding: CipherEncoding = "base64",
) -> str:
key_bytes = _normalize_key(_decode_value(key, key_encoding), key_size)
iv_bytes = _decode_value(iv, iv_encoding) if iv is not None else None
data = _decode_ciphertext(ciphertext, input_encoding)
cipher = _build_cipher(key_bytes, mode, iv_bytes)
decrypted = cipher.decrypt(data)
if mode in {"ECB", "CBC"}:
decrypted = _pkcs7_unpad(decrypted) if padding == "PKCS7" else _zero_unpad(decrypted)
return decrypted.decode("utf-8", errors="replace")
# Example usage
key_hex = "00112233445566778899aabbccddeeff"
iv_hex = "0102030405060708090a0b0c0d0e0f10"
cipher = aes_encrypt(
"hello",
key_hex,
mode="CBC",
padding="PKCS7",
key_encoding="hex",
iv=iv_hex,
iv_encoding="hex",
key_size=16,
output_encoding="hex",
)
print(cipher)
plain = aes_decrypt(
cipher,
key_hex,
mode="CBC",
padding="PKCS7",
key_encoding="hex",
iv=iv_hex,
iv_encoding="hex",
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
from Crypto.Util import Counter
Mode = Literal["ECB", "CBC", "CFB", "OFB", "CTR"]
Padding = Literal["PKCS7", "Zero"]
KeyEncoding = Literal["utf8", "hex"]
CipherEncoding = Literal["hex", "base64"]
BLOCK_SIZE = 16
def _normalize_key(key: bytes, key_size: int) -> bytes:
padded = key[:key_size].ljust(key_size, b"\x00")
return padded
def _normalize_iv(iv: bytes | None) -> bytes:
return (iv or b"")[:BLOCK_SIZE].ljust(BLOCK_SIZE, b"\x00")
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 _pkcs7_pad(data: bytes) -> bytes:
pad_len = BLOCK_SIZE - (len(data) % BLOCK_SIZE)
return data + bytes([pad_len] * pad_len)
def _pkcs7_unpad(data: bytes) -> bytes:
if not data:
return data
pad_len = data[-1]
if pad_len < 1 or pad_len > BLOCK_SIZE:
raise ValueError("Invalid PKCS7 padding")
if data[-pad_len:] != bytes([pad_len] * pad_len):
raise ValueError("Invalid PKCS7 padding")
return data[:-pad_len]
def _zero_pad(data: bytes) -> bytes:
if len(data) % BLOCK_SIZE == 0:
return data
pad_len = BLOCK_SIZE - (len(data) % BLOCK_SIZE)
return data + b"\x00" * pad_len
def _zero_unpad(data: bytes) -> bytes:
return data.rstrip(b"\x00")
def _build_cipher(key: bytes, mode: Mode, iv: bytes | None) -> AES:
if mode == "ECB":
return AES.new(key, AES.MODE_ECB)
if mode == "CBC":
return AES.new(key, AES.MODE_CBC, iv=_normalize_iv(iv))
if mode == "CFB":
return AES.new(key, AES.MODE_CFB, iv=_normalize_iv(iv), segment_size=128)
if mode == "OFB":
return AES.new(key, AES.MODE_OFB, iv=_normalize_iv(iv))
counter = Counter.new(128, initial_value=int.from_bytes(_normalize_iv(iv), "big"))
return AES.new(key, AES.MODE_CTR, counter=counter)
def aes_encrypt(
plaintext: str,
key: str,
*,
mode: Mode = "CBC",
padding: Padding = "PKCS7",
key_encoding: KeyEncoding = "hex",
iv: str | None = None,
iv_encoding: KeyEncoding = "hex",
key_size: int = 32,
output_encoding: CipherEncoding = "base64",
) -> str:
key_bytes = _normalize_key(_decode_value(key, key_encoding), key_size)
iv_bytes = _decode_value(iv, iv_encoding) if iv is not None else None
data = plaintext.encode("utf-8")
if mode in {"ECB", "CBC"}:
data = _pkcs7_pad(data) if padding == "PKCS7" else _zero_pad(data)
cipher = _build_cipher(key_bytes, mode, iv_bytes)
encrypted = cipher.encrypt(data)
return _encode_ciphertext(encrypted, output_encoding)
def aes_decrypt(
ciphertext: str,
key: str,
*,
mode: Mode = "CBC",
padding: Padding = "PKCS7",
key_encoding: KeyEncoding = "hex",
iv: str | None = None,
iv_encoding: KeyEncoding = "hex",
key_size: int = 32,
input_encoding: CipherEncoding = "base64",
) -> str:
key_bytes = _normalize_key(_decode_value(key, key_encoding), key_size)
iv_bytes = _decode_value(iv, iv_encoding) if iv is not None else None
data = _decode_ciphertext(ciphertext, input_encoding)
cipher = _build_cipher(key_bytes, mode, iv_bytes)
decrypted = cipher.decrypt(data)
if mode in {"ECB", "CBC"}:
decrypted = _pkcs7_unpad(decrypted) if padding == "PKCS7" else _zero_unpad(decrypted)
return decrypted.decode("utf-8", errors="replace")
def run_tests() -> None:
key = "00112233445566778899aabbccddeeff"
iv = "0102030405060708090a0b0c0d0e0f10"
cipher = aes_encrypt(
"hello",
key,
mode="CBC",
padding="PKCS7",
key_encoding="hex",
iv=iv,
iv_encoding="hex",
key_size=16,
output_encoding="hex",
)
recovered = aes_decrypt(
cipher,
key,
mode="CBC",
padding="PKCS7",
key_encoding="hex",
iv=iv,
iv_encoding="hex",
key_size=16,
input_encoding="hex",
)
assert recovered == "hello"
print("AES tests passed")
if __name__ == "__main__":
run_tests()