AES-256
PyCrypto
encryption
decryption
Python

Encrypt and decrypt using PyCrypto AES-256

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

If you need AES-256 in Python today, the practical answer is to use PyCryptodome, not the old unmaintained PyCrypto package. AES-256 means a 32-byte key, but secure encryption also requires the right mode, a unique nonce or IV, and integrity protection so decryption can detect tampering.

Use an Authenticated Mode

A common beginner mistake is to focus only on “AES-256” and ignore the mode of operation. Modern code should usually use an authenticated mode such as GCM rather than older patterns like ECB or CBC without a MAC.

With GCM, you get both encryption and integrity verification.

python
1from Crypto.Cipher import AES
2from Crypto.Random import get_random_bytes
3
4key = get_random_bytes(32)  # 32 bytes = AES-256
5plaintext = b"secret message"
6
7cipher = AES.new(key, AES.MODE_GCM)
8ciphertext, tag = cipher.encrypt_and_digest(plaintext)
9nonce = cipher.nonce
10
11print(len(key), ciphertext, tag, nonce)

That encrypts the message and produces three values you must keep for decryption: the ciphertext, the authentication tag, and the nonce.

Decrypt and Verify

Decryption must verify the tag. If you skip verification, you lose one of the main security benefits of GCM.

python
1from Crypto.Cipher import AES
2
3cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
4decrypted = cipher.decrypt_and_verify(ciphertext, tag)
5print(decrypted.decode())

If the ciphertext, tag, or nonce is wrong, decrypt_and_verify raises an exception instead of silently returning corrupted data.

If You Start From a Password

Passwords are not AES keys. If the input is a human password, derive a 32-byte key using a key-derivation function such as scrypt or PBKDF2.

python
1from Crypto.Protocol.KDF import scrypt
2from Crypto.Random import get_random_bytes
3
4password = b"correct horse battery staple"
5salt = get_random_bytes(16)
6key = scrypt(password, salt, key_len=32, N=2**14, r=8, p=1)

Store the salt alongside the encrypted output. During decryption, the same password and stored salt reproduce the key.

Packaging the Encrypted Output

A simple pattern is to store all required values together.

python
1blob = {
2    "salt": salt.hex(),
3    "nonce": nonce.hex(),
4    "ciphertext": ciphertext.hex(),
5    "tag": tag.hex(),
6}
7print(blob)

Any format is fine as long as decryption has access to everything it needs. The key point is that nonce and tag are not optional metadata. They are required.

Why Not Legacy PyCrypto

The title may mention PyCrypto because many old answers do. In practice, the maintained drop-in replacement is PyCryptodome.

bash
pip install pycryptodome

That gives you the Crypto namespace used in the examples above while avoiding an abandoned package.

What to Avoid

Avoid ECB mode entirely. It reveals patterns in the plaintext and is not acceptable for real security-sensitive use.

Also avoid reusing a nonce with the same key in GCM mode. Nonce reuse can break the security guarantees badly.

Finally, do not hardcode keys in source code or derive them by simply padding a password string to 32 bytes.

If you need key rotation, version your encrypted payload format so old ciphertext can still be decrypted safely during migration.

Common Pitfalls

The most common mistake is using AES without an authenticated mode or without a separate integrity check.

Another mistake is confusing “32-character password” with “32-byte cryptographic key.” Passwords should go through a derivation function first.

Developers also often forget to store the nonce or authentication tag, which makes correct decryption impossible.

Summary

  • Use PyCryptodome, not legacy PyCrypto, for modern Python AES work.
  • AES-256 requires a 32-byte key.
  • Prefer authenticated modes such as GCM.
  • If the source secret is a password, derive the key with scrypt or PBKDF2.
  • Store the nonce, ciphertext, tag, and salt together so decryption can succeed safely.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.