你是不是觉得只要对数据加密后数据就一定不会被篡改了?那你就大错特错了。
首先,我们来看一个神奇的实验:
from __future__ import annotations
from cryptography.hazmat.primitives.ciphers import Cipher , algorithms , modes
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.backends import default_backend
# ==================================================
# AES-CBC-256 核心加解密(非AEAD)
# ==================================================
def aes_cbc_encrypt(plain_data: bytes , key: bytes , iv: bytes) -> bytes:
"""CBC加密(需要PKCS7填充)"""
# 添加填充
padder = padding.PKCS7(128).padder()
padded_data = padder.update(plain_data) + padder.finalize()
# 加密
cipher = Cipher(algorithms.AES(key) , modes.CBC(iv) , backend = default_backend())
encryptor = cipher.encryptor()
return encryptor.update(padded_data) + encryptor.finalize()
def aes_cbc_decrypt(ciphertext: bytes , key: bytes , iv: bytes) -> bytes:
"""CBC解密(无完整性验证)"""
# 解密
cipher = Cipher(algorithms.AES(key) , modes.CBC(iv) , backend = default_backend())
decryptor = cipher.decryptor()
padded_plaintext = decryptor.update(ciphertext) + decryptor.finalize()
# 移除填充
unpadder = padding.PKCS7(128).unpadder()
return unpadder.update(padded_plaintext) + unpadder.finalize()
if __name__ == "__main__":
# 固定输入值(实际使用中应为随机值)
plaintext = b"Transfer $100 to Alice"
key = b"12345678901234567890123456789012" # 256-bit key,仅用作测试
iv = b"1234567890123456" # 16 字节 CBC IV
print("===== AES-CBC-256 演示 =====")
print(f'原始明文数据:{plaintext.hex()}')
# CBC加密
cbc_ciphertext = aes_cbc_encrypt(plaintext , key , iv)
print(f"密文: {cbc_ciphertext.hex()}")
# CBC解密(原始数据)
cbc_decrypted = aes_cbc_decrypt(cbc_ciphertext , key , iv)
print(f"对正常的密文解密后的结果: {cbc_decrypted.hex()}, 解密成功:{cbc_decrypted.hex() == plaintext.hex()}")
# 篡改第1个字节(在真实攻击中更隐蔽)
# 如果改末尾的字节则可能会解填充失败,会显式的抛出异常
tampered_ciphertext = bytes([ cbc_ciphertext[ 0 ] ^ 0x01 ]) + cbc_ciphertext[ 1: ]
# 解密被篡改的密文 - 不报错但得到错误结果
tampered_decrypted = aes_cbc_decrypt(tampered_ciphertext , key , iv)
print(
f"篡改后的密文解密出的结果: {tampered_decrypted.hex()},"
f"解密成功: {tampered_decrypted.hex() == plaintext.hex()},",
f"解出的数据错误但解密过程无异常")
猜一猜运行代码后会不会抛出异常?
答案是:不会!!!
AES-CBC等传统加密模式统统都面临着以下的困境:
AEAD(Authenticated Encryption with Associated Data)的出现彻底改变了游戏规则,它同时做到三件事:
from __future__ import annotations
from typing import Tuple
from cryptography.hazmat.primitives.ciphers import Cipher , algorithms , modes
from cryptography.hazmat.backends import default_backend
# ==================================================
# AES-GCM-256 核心加解密(AEAD模式)
# ==================================================
def aes_gcm_encrypt(plain_data: bytes , key: bytes , nonce: bytes , ad: bytes) -> Tuple[ bytes , bytes ]:
"""GCM加密(返回密文和认证标签)"""
cipher = Cipher(algorithms.AES(key) , modes.GCM(nonce) , backend = default_backend())
encryptor = cipher.encryptor()
# 添加关联数据认证
if ad:
encryptor.authenticate_additional_data(ad)
# 加密并获取认证标签
ciphertext = encryptor.update(plain_data) + encryptor.finalize()
return ciphertext , encryptor.tag
def aes_gcm_decrypt(ciphertext: bytes , tag: bytes , key: bytes , nonce: bytes , ad: bytes) -> bytes:
"""GCM解密(带完整性验证)"""
cipher = Cipher(algorithms.AES(key) , modes.GCM(nonce , tag) , backend = default_backend())
decryptor = cipher.decryptor()
# 添加关联数据认证
if ad:
decryptor.authenticate_additional_data(ad)
# 解密(如果验证失败会抛出异常)
return decryptor.update(ciphertext) + decryptor.finalize()
if __name__ == "__main__":
# 固定输入值(实际使用中应为随机值)
plaintext = b"Secret message! AEAD is awesome!"
key = b"12345678901234567890123456789012" # 256-bit key 仅用作测试
nonce = b"123456789012" # GCM 12 字节 nonce
ad = b"Additional data" # GCM关联数据
print("\n===== AES-GCM-256 演示 =====")
print(f'原始明文数据: {plaintext.hex()}')
# GCM加密
gcm_ciphertext , tag = aes_gcm_encrypt(plaintext , key , nonce , ad)
print(f"密文数据: {gcm_ciphertext.hex()}")
print(f"认证标签: {tag.hex()}")
# GCM解密(原始数据)
gcm_decrypted = aes_gcm_decrypt(gcm_ciphertext , tag , key , nonce , ad)
print(f"正常的密文解密结果: {gcm_decrypted.decode()}, 解密成功:{gcm_decrypted.hex() == plaintext.hex()}")
# 尝试篡改密文的第一个字节
tampered_gcm_ciphertext = bytes([ gcm_ciphertext[ 0 ] ^ 0x01 ]) + gcm_ciphertext[ 1: ]
# 解密篡改后的密文 - 应该失败
try:
aes_gcm_decrypt(tampered_gcm_ciphertext , tag , key , nonce , ad)
print("解密成功 (不应该发生)")
except Exception as e:
print(f"篡改测试: {e} 解密失败,AEAD检测到密文篡改!")
# 尝试篡改关联数据
try:
aes_gcm_decrypt(gcm_ciphertext , tag , key , nonce , b"tampered_ad")
print("解密成功 (不应该发生)")
except Exception as e:
print(f"AD篡改测试: {e} 解密失败,AEAD检测到AD篡改!")
我们可以清楚的看到,对于正确的密文,可以正确解密出明文;而对于被篡改后的密文,则会捕获解密失败的异常。
这就是 AEAD 的特殊功效:
欢迎光临 奶昔论坛 (https://bbs.moecloud.cn/) | Powered by Discuz! X3.5 |