AES 加密¶
AES 加密,支持 ECB、CBC、CTR 和 GCM 模式,适用于 128/192/256 位密钥——零依赖,仅标准库,Python 3.10+。
可替代:
pycryptodome(AES 部分)、cryptography(AES 部分)
概述¶
AES 模块提供多种模式和密钥长度的 AES 加解密功能。提供两个可互换的实现:
| 文件 | 说明 | 依赖 |
|---|---|---|
aes.py |
通过 ctypes 调用系统 OpenSSL(默认) |
运行时需要系统安装 libcrypto |
aes_python.py |
纯 Python 实现 | 无(仅标准库) |
两个文件暴露完全相同的公开 API,可以直接替换,无需修改调用代码。
支持的模式¶
| 模式 | 填充 | IV/Nonce | 适用场景 |
|---|---|---|---|
| ECB | PKCS7 | 无 | 简单加密(不推荐用于结构化数据) |
| CBC | PKCS7 | 16 字节 IV | 通用分组加密 |
| CTR | 无 | 16 字节 nonce | 流式加密,可并行化 |
| GCM | 无 | 12 字节 nonce(推荐) | 认证加密(AEAD) |
支持的密钥长度¶
所有模式均支持 AES-128(16 字节)、AES-192(24 字节)和 AES-256(32 字节)密钥。
如何在你的项目中使用¶
只需将所需的 .py 文件复制到你的项目中:
# 复制整个包(OpenSSL 为默认,纯 Python 自动降级)
zerodep add aes --nested
# 或者复制单独的文件
cp aes/aes.py your_project/ # OpenSSL(默认)
cp aes/aes_python.py your_project/ # 纯 Python 后备
然后直接导入——包会优先使用 OpenSSL,如果不可用则自动降级到纯 Python:
from aes import aes_ecb_encrypt, aes_cbc_encrypt, aes_ctr_encrypt, aes_gcm_encrypt
from aes import BACKEND # "openssl" or "python"
使用示例¶
ECB 模式(基本加解密)¶
from aes import aes_ecb_encrypt, aes_ecb_decrypt
key = b"0123456789abcdef" # 16-byte key (AES-128)
message = b"Hello, World!"
ciphertext = aes_ecb_encrypt(message, key)
plaintext = aes_ecb_decrypt(ciphertext, key)
assert plaintext == message
同样支持 AES-192 和 AES-256:
key_256 = b"0123456789abcdef" * 2 # 32-byte key (AES-256)
ciphertext = aes_ecb_encrypt(b"secret", key_256)
CBC 模式¶
import os
from aes import aes_cbc_encrypt, aes_cbc_decrypt
key = os.urandom(32) # AES-256
iv = os.urandom(16) # 16-byte IV
message = b"Confidential data"
ciphertext = aes_cbc_encrypt(message, key, iv)
plaintext = aes_cbc_decrypt(ciphertext, key, iv)
assert plaintext == message
CTR 模式¶
import os
from aes import aes_ctr_encrypt, aes_ctr_decrypt
key = os.urandom(16)
nonce = os.urandom(16) # 16-byte initial counter block
data = b"Stream cipher style"
ciphertext = aes_ctr_encrypt(data, key, nonce)
assert len(ciphertext) == len(data) # No padding needed
plaintext = aes_ctr_decrypt(ciphertext, key, nonce)
assert plaintext == data
GCM 模式(认证加密)¶
import os
from aes import aes_gcm_encrypt, aes_gcm_decrypt
key = os.urandom(32) # AES-256
nonce = os.urandom(12) # 12-byte nonce (recommended)
header = b"metadata" # Additional Authenticated Data (AAD)
message = b"Secret payload"
# Encrypt -- returns (ciphertext, authentication_tag)
ciphertext, tag = aes_gcm_encrypt(message, key, nonce, aad=header)
# Decrypt -- raises ValueError if tampered
plaintext = aes_gcm_decrypt(ciphertext, key, nonce, tag, aad=header)
assert plaintext == message
检测数据篡改:
import pytest
tampered = bytes([ciphertext[0] ^ 0xFF]) + ciphertext[1:]
with pytest.raises(ValueError, match="authentication failed"):
aes_gcm_decrypt(tampered, key, nonce, tag, aad=header)
检查当前使用的后端¶
__init__.py 会自动优先选择 OpenSSL,不可用时降级到纯 Python。你可以检查当前激活的后端:
from aes import BACKEND, aes_ecb_encrypt
print(BACKEND) # "openssl" or "python"
ciphertext = aes_ecb_encrypt(b"secret data", b"0123456789abcdef")
使用 CBC 加密文件¶
import os
from aes import aes_cbc_encrypt, aes_cbc_decrypt
key = os.urandom(32)
iv = os.urandom(16)
# Encrypt
with open("input.bin", "rb") as f:
data = f.read()
ct = aes_cbc_encrypt(data, key, iv)
with open("input.bin.enc", "wb") as f:
f.write(iv + ct) # Prepend IV for later retrieval
# Decrypt
with open("input.bin.enc", "rb") as f:
raw = f.read()
iv_read, ct_read = raw[:16], raw[16:]
pt = aes_cbc_decrypt(ct_read, key, iv_read)
API 参考¶
ECB 模式¶
aes_ecb_encrypt(data: bytes, key: bytes) -> bytes
aes_ecb_decrypt(data: bytes, key: bytes) -> bytes
aes_ecb_padded_size(plaintext_size: int) -> int
- key:16、24 或 32 字节
- PKCS7 填充自动应用
aes_ecb_padded_size()计算 PKCS7 填充后的密文大小,无需实际执行加密——适用于预分配缓冲区aes128_ecb_encrypt/aes128_ecb_decrypt作为向后兼容的别名可用
CBC 模式¶
aes_cbc_encrypt(data: bytes, key: bytes, iv: bytes) -> bytes
aes_cbc_decrypt(data: bytes, key: bytes, iv: bytes) -> bytes
- key:16、24 或 32 字节
- iv:必须恰好 16 字节
- PKCS7 填充自动应用
CTR 模式¶
aes_ctr_encrypt(data: bytes, key: bytes, nonce: bytes) -> bytes
aes_ctr_decrypt(data: bytes, key: bytes, nonce: bytes) -> bytes
- key:16、24 或 32 字节
- nonce:16 字节初始计数器块
- 无填充——输出长度等于输入长度
aes_ctr_encrypt和aes_ctr_decrypt是同一个函数(CTR 模式加解密对称)
GCM 模式¶
aes_gcm_encrypt(data: bytes, key: bytes, nonce: bytes,
aad: bytes = b"", tag_length: int = 16) -> tuple[bytes, bytes]
aes_gcm_decrypt(data: bytes, key: bytes, nonce: bytes,
tag: bytes, aad: bytes = b"") -> bytes
- key:16、24 或 32 字节
- nonce:推荐 12 字节(支持任意长度)
- aad:附加认证数据(受完整性保护但不加密)
- tag_length:4-16 字节(默认 16)
- 加密时返回
(ciphertext, tag) - 解密时若认证标签验证失败,抛出
ValueError("authentication failed")
OpenSSL 变体细节¶
aes.py(OpenSSL 变体)使用 Python 内置的 ctypes 模块调用系统的 OpenSSL libcrypto 库。由于 Python 自身就依赖 OpenSSL(ssl 和 hashlib 模块都链接到 libcrypto),任何标准的 Python 3.10+ 安装都已经包含 libcrypto——无需额外安装任何软件。
运行时按以下顺序查找库文件:
ctypes.util.find_library("crypto")-- 跨平台标准方式。- 平台特定的回退路径:
- Linux:
libcrypto.so.3,libcrypto.so.1.1,libcrypto.so - macOS:
/opt/homebrew/lib/libcrypto.dylib,/usr/local/lib/libcrypto.dylib,libcrypto.dylib - Windows:
libcrypto-3-x64.dll,libcrypto-3.dll,libcrypto-1_1-x64.dll,libcrypto-1_1.dll
- Linux:
如果找不到 libcrypto,将在导入时抛出 OSError。
注意事项¶
ECB 模式的安全性
ECB 模式独立加密每个 16 字节的数据块,这意味着相同的明文块会产生相同的密文块。因此 ECB 不适合加密结构化或重复性数据。请改用 CBC、CTR 或 GCM。
推荐模式
对于大多数应用场景,推荐使用 GCM 模式——它同时提供加密和认证(AEAD),能够防止窃听和篡改。GCM 也是 TLS 1.3 的默认加密模式。
性能说明
纯 Python 实现(aes_python.py)设计简洁,偏重教学目的。对于大量数据,它比原生实现慢数个数量级。任何对性能敏感的场景,请使用默认的 aes.py(OpenSSL)。
- 密钥长度: 必须为 16、24 或 32 字节。传入无效长度的密钥会抛出
ValueError。 - PKCS7 填充: 在 ECB 和 CBC 模式下自动应用。CTR 和 GCM 不使用填充。
- Python 版本: 需要 Python 3.10+。
- IV/nonce 管理: IV 和 nonce 不会自动生成。请使用
os.urandom()生成,并将其与密文一起存储或传输。
性能测试¶
与 pycryptodome 在 ECB、CBC、CTR 和 GCM 模式下进行对比。OpenSSL ctypes 比 pycryptodome 快约 2 倍;纯 Python 仅适合小数据量。
详见 AES 性能测试。