杰瑞科技汇

Python AES 填充方式有哪些?

Python AES 加密详解:Padding(填充)的完整指南与最佳实践

在当今数据安全至关重要的时代,加密技术是开发者必备技能之一,AES(Advanced Encryption Standard,高级加密标准)作为对称加密算法的标杆,广泛应用于数据保护、通信安全等领域,在使用 Python 进行 AES 加密时,一个常常让开发者困惑但又至关重要的概念就是 Padding(填充)

Python AES 填充方式有哪些?-图1
(图片来源网络,侵删)

本文将深入探讨 Python AES 加密中的 Padding 机制,解答你关于 “为什么需要 Padding?”、“有哪些 Padding 方式?”、“如何在 Python 中正确处理 Padding?” 等所有疑问,并提供从基础到实践的完整代码示例。


为什么 AES 加密需要 Padding(填充)?

要理解 Padding,首先需要明白 AES 算法的一个基本特性:AES 是分组加密算法

这意味着 AES 加密的数据必须是固定长度的“块”(Block),AES 标准支持块长度为 128 位(16 字节),这意味着,无论你加密的数据有多长,它都会被分割成一个个 16 字节的数据块进行独立加密。

问题来了:如果待加密的数据长度不是 16 字节的整数倍怎么办?

Python AES 填充方式有哪些?-图2
(图片来源网络,侵删)

你想加密字符串 "Hello, AES!",它的长度是 12 字节,无法被 16 整除,Padding 就登场了。

Padding 的核心作用:在加密前,对明文数据进行填充,使其总长度成为 AES 块长度的整数倍(16 字节),解密后,再移除这些填充数据,恢复原始明文。

不使用 Padding 的后果:如果数据长度不足,某些加密模式(如 ECB、CBC)会直接报错,导致加密过程无法进行。


常见的 Padding 方式

Padding 方式有多种,选择哪种方式取决于你的应用场景和兼容性要求,以下是几种最主流的 Padding 方式:

Python AES 填充方式有哪些?-图3
(图片来源网络,侵删)

PKCS#7 Padding (最常用)

这是目前应用最广泛的 Padding 方式,也是许多加密库(如 Python 的 cryptography 库)的默认选择。

  • 规则:假设需要填充 n 个字节,那么就在末尾填充 n 个字节,每个字节的值都是 n
  • 示例
    • 原始数据:"Hello, AES!" (12 字节)
    • 需要填充的字节数:16 - 12 = 4 字节
    • 填充后的数据:"Hello, AES!" + \x04 \x04 \x04 \x04
    • 解密后,通过检查最后一个字节 \x04 的值,就知道需要移除最后 4 个字节。

ANSI X.923 Padding

这是一种与 PKCS#7 类似但略有不同的标准。

  • 规则:需要填充 n 个字节,最后一个字节填充 n,其余 n-1 个字节全部填充 \x00 (零)。
  • 示例
    • 原始数据:"Hello, AES!" (12 字节)
    • 需要填充的字节数:4 字节
    • 填充后的数据:"Hello, AES!" + \x00 \x00 \x00 \x04

ISO 10126 Padding

这是一种较老的标准,现在已不推荐用于新系统。

  • 规则:需要填充 n 个字节,最后一个字节填充 n,其余 n-1 个字节填充随机数据。
  • 示例
    • 原始数据:"Hello, AES!" (12 字节)
    • 需要填充的字节数:4 字节
    • 填充后的数据:"Hello, AES!" + (随机字节) \x00 \x00 \x04

Zero Padding (零填充)

最简单的方式,但存在安全风险。

  • 规则:直接在数据末尾填充 \x00 直到长度为 16 的倍数。
  • 致命缺陷:如果原始数据本身就以 \x00 解密时将无法区分哪些是原始数据,哪些是填充的零,导致数据损坏。强烈不建议在加密场景下使用 Zero Padding。

在 Python 中使用 AES 加密与 Padding (实践指南)

Python 中有两个主流的加密库:pycryptodomecryptography,我们推荐使用 cryptography,因为它更现代、设计更合理,是 Python 官方推荐的加密库。

环境准备

安装 cryptography 库:

pip install cryptography

完整代码示例:AES-CBC 模式与 PKCS#7 Padding

AES 加密通常需要与一个“工作模式”(Mode)和“初始化向量”(IV)结合使用。CBC 模式 是最常见的工作模式之一,它要求 IV 长度与块长度相同(16 字节)。

下面是一个完整的、包含加密和解密的 Python 脚本,我们将重点分析 Padding 的处理。

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.backends import default_backend
import os
# --- 1. 准备密钥和初始化向量 ---
# AES 密钥长度必须是 16, 24, 或 32 字节 (对应 AES-128, AES-192, AES-256)
key = os.urandom(32) # 生成一个 32 字节的随机密钥 (AES-256)
# IV 长度必须与块长度相同,对于 AES 是 16 字节
# 注意:在实际应用中,IV 应该是随机且唯一的,但不需要保密
iv = os.urandom(16)
# --- 2. 待加密的原始数据 ---
plaintext = b"This is a secret message that needs to be encrypted."
print(f"原始数据: {plaintext}")
print(f"原始数据长度: {len(plaintext)} bytes")
# --- 3. 加密过程 ---
# 3.1 创建一个 PKCS#7 padding器
# block_size 以字节为单位,对于 AES 是 16
padder = padding.PKCS7(algorithms.AES.block_size).padder()
# 3.2 对数据进行填充
padded_data = padder.update(plaintext) + padder.finalize()
print(f"\n填充后数据长度: {len(padded_data)} bytes (必须是 16 的倍数)")
print(f"填充后数据: {padded_data}")
# 3.3 创建 AES-CBC 加密器
cipher = Cipher(
    algorithms.AES(key),
    modes.CBC(iv),
    backend=default_backend()
)
encryptor = cipher.encryptor()
# 3.4 执行加密
ciphertext = encryptor.update(padded_data) + encryptor.finalize()
print(f"\n加密后数据: {ciphertext}")
print(f"加密后数据长度: {len(ciphertext)} bytes")
# --- 4. 解密过程 ---
# 4.1 创建 AES-CBC 解密器
decryptor = cipher.decryptor()
# 4.2 执行解密
decrypted_padded_data = decryptor.update(ciphertext) + decryptor.finalize()
print(f"\n解密后的填充数据: {decrypted_padded_data}")
# 4.3 移除填充数据
unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
unpadded_data = unpadder.update(decrypted_padded_data) + unpadder.finalize()
print(f"\n最终解密数据: {unpadded_data}")
print(f"最终解密数据长度: {len(unpadded_data)} bytes")
# --- 5. 验证 ---
assert plaintext == unpadded_data
print("\n验证成功:解密后的数据与原始数据完全一致!")

代码关键点解析

  1. padding.PKCS7(algorithms.AES.block_size)

    • 这是 cryptography 库处理 Padding 的核心,我们创建了一个 PKCS#7 填充器。
    • algorithms.AES.block_size 会自动返回 16,这是 AES 的块大小,使代码更具可读性和可维护性。
  2. .padder().unpadder()

    • 填充器(padder)用于加密前,它有一个 update() 方法来处理数据,和一个 finalize() 方法来完成填充并返回剩余数据。
    • 解填充器(unpadder)用于解密后,它同样有 update()finalize() 方法,用于移除填充并返回原始数据。
  3. CBC 模式与 IV

    • 我们使用了 modes.CBC(iv) 来指定 CBC 模式。
    • 重要:IV 必须与接收方共享,但它本身不是秘密,IV 会和密文一起传输。

常见问题与最佳实践

Q1: 加密后的数据长度为什么会比原始数据长?

因为加密后的数据长度取决于填充后的数据长度,如果原始数据长度 L 不是 16 的倍数,加密后的长度就是 ceil(L / 16) * 16,IV 的长度(16字节)也需要考虑,IV 会和密文一起存储或传输。

Q2: 如何处理恰好是 16 字节倍数的数据?

Padding 机制会优雅地处理这种情况,如果数据正好是 16 字节,PKCS#7 填充器会添加一个完整的块,即 16 个值为 16 的字节(\x10 \x10 ... \x10),解密时,解填充器会识别并移除这 16 个字节。

Q3: 如何安全地存储和传输密钥和 IV?

  • 密钥:这是最核心的秘密,绝不能硬编码在代码中或明文传输,可以使用密钥派生函数(如 PBKDF2、scrypt、Argon2)从用户密码生成密钥,或者使用密钥管理系统(KMS)。
  • IV:IV 不需要保密,但必须是唯一的,对于每个加密操作,都应该生成一个新的随机 IV,IV 会被作为前缀附加到密文前面一起存储或传输。

最佳实践总结

  1. 使用 cryptography:它是 Python 加密的事实标准,安全且易于使用。
  2. 始终使用 PKCS#7 Padding:除非你有特殊的兼容性要求,否则 PKCS#7 是最安全、最可靠的选择。
  3. 选择合适的工作模式:避免使用不安全的 ECB 模式。CBCGCM (Galois/Counter Mode,提供认证加密) 是更好的选择。
  4. 使用随机 IV:每次加密都生成一个新的随机 IV。
  5. 妥善保管密钥:密钥是安全体系的基石,必须严格保护。
  6. 不要自己实现加密算法:使用成熟的、经过审查的库,而不是尝试自己编写加密逻辑。

理解并正确处理 Padding 是掌握 Python AES 加密的关键一步,本文从 Padding 的基本原理出发,详细介绍了主流的 Padding 方式,并通过一个完整的代码示例展示了在 cryptography 库中如何优雅地处理加密和解密过程中的填充与解填充。

希望这篇详尽的指南能帮助你彻底搞懂 python aes padding,并在你的项目中安全、高效地应用 AES 加密技术,安全无小事,细节决定成败。


分享:
扫描分享到社交APP
上一篇
下一篇