如何用Python cryptography重写Crypto-JS的AES解密代码?
Crypto-JS AES解密代码转Python cryptography实现方案
原JS代码逻辑
const data = 'base 64 encoded encrypted data'; const salt = 'XB7sHH26Hn&FmPLxnjGccKTfPV(yk'; const pass = '.. password ..'; // CryptoJS.PBKDF2默认生成256位密钥,转十六进制字符串后拼接固定字符 const psw = `${CryptoJS.PBKDF2(pass, salt)}(tXntTbJFzh]4EuQVmjzM9GXHCth8`; // 传入字符串密码,CryptoJS自动用EVP_BytesToKey派生密钥和IV进行解密 let plaintext = CryptoJS.AES.decrypt(data, psw).toString(CryptoJS.enc.Utf8);
你的Python代码问题分析
你目前遇到的三个问题及原因:
- 问题1:Crypto-JS中
WordArray(PBKDF2返回值)在模板字符串中会自动调用toString(),默认输出十六进制字符串,你的这个判断是正确的。 - 问题2:Crypto-JS调用
AES.decrypt(data, 字符串密码)时,不会直接用该字符串做密钥,而是通过EVP_BytesToKey算法(MD5哈希、1次迭代、无盐)生成AES密钥和IV,默认对应AES-256(32字节密钥+16字节IV)。 - 问题3:你直接将拼接后的长字符串作为AES密钥,而AES仅支持16/24/32字节密钥(对应128/192/256位),61字节的密钥自然不符合要求。
修正后的Python代码
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC from cryptography.hazmat.backends import default_backend import base64 import hashlib def evp_bytes_to_key(password, key_len, iv_len): """实现OpenSSL EVP_BytesToKey算法,MD5哈希、1次迭代、无盐""" password_bytes = password.encode('utf-8') key = b'' iv = b'' tmp = b'' while len(key) < key_len or len(iv) < iv_len: tmp = hashlib.md5(tmp + password_bytes).digest() key += tmp[:min(len(tmp), key_len - len(key))] if len(iv) < iv_len: iv += tmp[:min(len(tmp), iv_len - len(iv))] return key, iv def unpad_pkcs7(data): """去除PKCS#7填充""" padding_len = data[-1] return data[:-padding_len] # 配置参数 data_b64 = 'base 64 encoded encrypted data' password = '.. password ..' salt = 'XB7sHH26Hn&FmPLxnjGccKTfPV(yk' fixed_str = '(tXntTbJFzh]4EuQVmjzM9GXHCth8' # 1. 计算PBKDF2并转十六进制字符串 kdf = PBKDF2HMAC( algorithm=hashes.SHA1(), length=32, # CryptoJS.PBKDF2默认生成256位(32字节)密钥 salt=salt.encode('utf-8'), iterations=1, backend=default_backend() ) pbkdf2_result = kdf.derive(password.encode('utf-8')) pbkdf2_hex = pbkdf2_result.hex() # 2. 拼接得到Crypto-JS中使用的密码字符串 psw_str = pbkdf2_hex + fixed_str # 3. 用EVP_BytesToKey生成AES密钥和IV key, iv = evp_bytes_to_key(psw_str, 32, 16) # 4. 解密过程 cipher_data = base64.b64decode(data_b64) cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend()) decryptor = cipher.decryptor() decrypted_raw = decryptor.update(cipher_data) + decryptor.finalize() # 5. 去除填充并转明文 plaintext = unpad_pkcs7(decrypted_raw).decode('utf-8') print(plaintext)
关键说明
- PBKDF2参数:CryptoJS.PBKDF2默认使用SHA1、1次迭代、生成256位密钥,所以Python中
length设为32(字节)。 - EVP_BytesToKey:这是OpenSSL传统的密钥派生算法,Crypto-JS在传入字符串密码时默认使用该逻辑,需要手动实现。
- PKCS#7填充:Crypto-JS的AES默认使用PKCS#7填充,解密后需要手动去除。
内容的提问来源于stack exchange,提问作者linkedin
相关产品推荐
相关产品推荐

