You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Bouncy Castle/CryptoJS与Pycryptodome:Python AES解密填充差异排查

AES CBC跨库解密填充问题解析

填充实现差异

Bouncy Castle与CryptoJS默认均采用PKCS#7填充(注:PKCS#5是PKCS#7的子集,针对8字节块大小;AES块为16字节,因此两者效果完全一致,部分库会混用这两个名称)。而Python生态的Pycryptodome、Cryptography库默认不会自动处理填充:

  • Pycryptodome需手动调用unpad方法指定填充规则,若忽略则直接返回包含填充字节的原始解密数据,触发后续处理错误。
  • Cryptography库默认按无填充模式解密,若未显式配置PKCS#7去填充逻辑,会直接抛出填充验证失败的异常。

少数场景下,若Bouncy Castle被显式配置为Zero填充(不足块大小的部分补0x00),而CryptoJS同步设置了对应填充规则,此时Python端若仍用PKCS#7去填充,必然出现错误。

Python正确解密方案

前置确认

需与加密端完全一致的核心参数:

  • 密钥:AES-128对应16字节长度,需确保编码(通常为UTF-8)与加密端一致
  • IV:CBC模式必须用16字节随机IV,多数场景下会拼接在密文头部(前16字节)
  • 填充方式:确认是PKCS#7还是Zero填充

1. Pycryptodome实现

PKCS#7填充场景

from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

# 示例:密文为Base64编码,需先解码;若为原始bytes则直接使用
ciphertext_b64 = "YOUR_CIPHERTEXT_BASE64"
ciphertext = bytes.fromhex(ciphertext_b64)  # 或用base64.b64decode,根据加密端编码方式调整
key = b"YOUR_16BYTE_KEY"  # 字符串转bytes用 key = "密钥字符串".encode("utf-8")
iv = ciphertext[:16]  # 从密文头部提取IV
real_ciphertext = ciphertext[16:]

# 解密并去填充
cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext = unpad(cipher.decrypt(real_ciphertext), AES.block_size)
print(plaintext.decode("utf-8"))

Zero填充场景

from Crypto.Cipher import AES

# 密钥、IV、密文处理同上
cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext = cipher.decrypt(real_ciphertext)
# 移除末尾的0x00填充
plaintext = plaintext.rstrip(b"\x00")
print(plaintext.decode("utf-8"))

2. Cryptography实现

PKCS#7填充场景

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.backends import default_backend

key = b"YOUR_16BYTE_KEY"
ciphertext = bytes.fromhex("YOUR_CIPHERTEXT_BASE64")
iv = ciphertext[:16]
real_ciphertext = ciphertext[16:]

# 解密
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
decryptor = cipher.decryptor()
decrypted_data = decryptor.update(real_ciphertext) + decryptor.finalize()

# 去PKCS#7填充
unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
plaintext = unpadder.update(decrypted_data) + unpadder.finalize()
print(plaintext.decode("utf-8"))

Zero填充场景

解密后直接调用plaintext.rstrip(b"\x00")移除填充字节即可,逻辑同Pycryptodome。

常见错误排查

  • 编码不匹配:加密端用UTF-8编码密钥/明文,Python端误用其他编码(如GBK),会导致密钥长度或内容错误。
  • IV未分离:未从密文中提取IV,直接用完整密文解密,会导致解密结果混乱且触发填充错误。
  • 填充规则误判:若加密端用Zero填充,Python端强行用PKCS#7去填充,会抛出ValueError异常,此时需检查加密端的Bouncy Castle配置。

内容的提问来源于stack exchange,提问作者Marek Marczak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 06:52:01