Introduction
The cryptography library provides cryptographic primitives for secure communication and data protection. This tutorial covers Fernet (symmetric encryption) and AES encryption.
Fernet Symmetric Encryption
from cryptography.fernet import Fernet
# Generate a key
key = Fernet.generate_key()
print(f"Key: {key}")
# Create Fernet cipher
cipher = Fernet(key)
# Encrypt data
message = b"Secret message"
encrypted = cipher.encrypt(message)
print(f"Encrypted: {encrypted}")
# Decrypt data
decrypted = cipher.decrypt(encrypted)
print(f"Decrypted: {decrypted}")
# With expiration
from cryptography.fernet import MultiFernet
# Encrypt with timestamp - automatically expires after 1 hour
token = cipher.encrypt_at_time(message, ttl=3600)
# Decrypt and verify expiration
try:
decrypted = cipher.decrypt(token, ttl=3600)
except Exception as e:
print(f"Token expired or invalid: {e}")
AES Encryption
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import os
def aes_encrypt(plaintext: bytes, key: bytes) -> tuple:
"""Encrypt with AES-CBC."""
iv = os.urandom(16) # Random initialization vector
cipher = Cipher(
algorithms.AES(key),
modes.CBC(iv),
backend=default_backend()
)
encryptor = cipher.encryptor()
# PKCS7 padding
padding_length = 16 - len(plaintext) % 16
padded = plaintext + bytes([padding_length] * padding_length)
ciphertext = encryptor.update(padded) + encryptor.finalize()
return iv, ciphertext
def aes_decrypt(ciphertext: bytes, key: bytes, iv: bytes) -> bytes:
"""Decrypt with AES-CBC."""
cipher = Cipher(
algorithms.AES(key),
modes.CBC(iv),
backend=default_backend()
)
decryptor = cipher.decryptor()
padded = decryptor.update(ciphertext) + decryptor.finalize()
# Remove padding
padding_length = padded[-1]
return padded[:-padding_length]
# Key must be 32 bytes for AES-256
key = os.urandom(32)
iv, encrypted = aes_encrypt(b"Secret data", key)
decrypted = aes_decrypt(encrypted, key, iv)
print(f"Decrypted: {decrypted}")
AES-GCM (Authenticated Encryption)
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
def encrypt_aes_gcm(plaintext: bytes, key: bytes) -> bytes:
"""Encrypt with AES-GCM."""
nonce = os.urandom(12) # 96-bit nonce
aesgcm = AESGCM(key)
ciphertext = aesgcm.encrypt(nonce, plaintext, None)
return nonce + ciphertext
def decrypt_aes_gcm(ciphertext: bytes, key: bytes) -> bytes:
"""Decrypt with AES-GCM."""
nonce = ciphertext[:12]
data = ciphertext[12:]
aesgcm = AESGCM(key)
return aesgcm.decrypt(nonce, data, None)
# Usage
key = os.urandom(32)
encrypted = encrypt_aes_gcm(b"Secret message", key)
decrypted = decrypt_aes_gcm(encrypted, key)
print(f"Decrypted: {decrypted}")
Key Management
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
def derive_key(password: str, salt: bytes) -> bytes:
"""Derive key from password using PBKDF2."""
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=100000,
backend=default_backend()
)
return kdf.derive(password.encode())
import base64
def encode_key(key: bytes) -> str:
"""Encode key for storage."""
return base64.urlsafe_b64encode(key).decode()
def decode_key(encoded: str) -> bytes:
"""Decode key from storage."""
return base64.urlsafe_b64decode(encoded.encode())
Practice Problems
- Implement file encryption with streaming
- Create encrypted database field storage
- Add key rotation support
- Implement message authentication codes (HMAC)
- Build a secure key storage system