前言
AES
有多种加密模式,本文选取了最常用的 CBC
模式
Cipher Block Chaining
密码块链模式
Python 3.11.8
cryptography 43.0.3
loguru 0.7.2
示例代码
# encoding: utf-8
# author: qbit
# date: 2024-10-28
# summary: 测试 AES 的加密和解密
import os
import random
import string
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from loguru import logger
def pad(data):
r"""填充函数, 确保数据块符合AES块大小要求"""
padder = padding.PKCS7(algorithms.AES.block_size).padder()
padded_data = padder.update(data) + padder.finalize()
return padded_data
def unpad(padded_data):
r"""解填充函数"""
unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
data = unpadder.update(padded_data) + unpadder.finalize()
return data
def gen_iv(length: int, mode: int = 0) -> bytes:
r"""Generate Initialization Vector, 生成初始化向量
length: 返回的 bytes 长度, 可选值 16/24/32
mode: 0 使用 os.urandom 生成
1 使用 random.choice 随机选择字符生成,字符包括 数字/小写字母/大写字母
0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
"""
match mode:
case 0:
return os.urandom(16)
case 1:
cand = f"{string.digits}{string.ascii_letters}" # candidate 候选字符
return "".join(random.choice(cand) for _ in range(length)).encode("ascii")
case _:
errMsg = f"Error mode: {mode}"
raise Exception(errMsg)
def encrypt_cbc(plaintext, key):
r"""加密函数, 以 CBC 模式加密"""
# 确保key是16, 24或32字节长
key = key.encode("utf-8")
# 生成随机的初始化向量 IV
iv = gen_iv(16, 1)
logger.debug(f"iv: {iv}")
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
encryptor = cipher.encryptor()
padded_plaintext = pad(plaintext.encode("utf-8"))
ciphertext = encryptor.update(padded_plaintext) + encryptor.finalize()
# 返回IV和密文,以便解密时使用
return iv + ciphertext
def decrypt_cbc(ciphertext, key):
r"""解密函数, 以 CBC 模式解密"""
key = key.encode("utf-8")
# 提取IV和密文
iv = ciphertext[:16]
ciphertext = ciphertext[16:]
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
decryptor = cipher.decryptor()
padded_plaintext = decryptor.update(ciphertext) + decryptor.finalize()
plaintext = unpad(padded_plaintext)
return plaintext.decode("utf-8")
if __name__ == "__main__":
plaintext = "hello qbit! 你好"
logger.debug(f"明文: {plaintext}")
key = "qbit_aestest_ibq" # 密钥长度必须是16, 24或32字节
logger.debug(f"key: {key}")
# 加密
ciphertext = encrypt_cbc(plaintext, key)
logger.debug(f"密文: {ciphertext.hex()}")
# 解密
decrypted_plaintext = decrypt_cbc(ciphertext, key)
logger.debug(f"解密后的明文: {decrypted_plaintext}")
- 为了便于在线验证,示例中生成初始化向量采用了随机选取
数字/小写字母/大写字母
的方式 - 输出结果
明文: hello qbit! 你好
key: qbit_aestest_ibq
iv: b'SPA09YWy8srAth5D'
密文: 53504130395957793873724174683544 9fdb9559c2724c89d60e606cd69d8635e5a2b7f2585767c2894f4bf04b95fa7c
解密后的明文: hello qbit! 你好
相关阅读
本文出自 qbit snap
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。