如何实现Python与TypeScript的AES加解密结果一致?
问题描述
我使用以下TypeScript代码实现AES加解密:
const AES = require('crypto-js/aes'); const Utf8 = require('crypto-js/enc-utf8'); export const encrypt = (text, passphrase) => { return AES.encrypt(text, passphrase).toString(); }; export const decrypt = (ciphertext, passphrase) => { const bytes = AES.decrypt(ciphertext, passphrase); const originalText = bytes.toString(Utf8); return originalText; };
需要在Python环境中解密TypeScript生成的密文,但当前基于cryptography库编写的代码无法得到正确结果:
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.scrypt import Scrypt from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import padding from os import urandom def derive_key_iv(password: str, salt: bytes, key_length=32, iv_length=16): # Key derivation function backend = default_backend() kdf = Scrypt( salt=salt, length=key_length + iv_length, n=2**14, r=8, p=1, backend=backend ) key_iv = kdf.derive(password.encode()) return key_iv[:key_length], key_iv[key_length:key_length+iv_length] def encrypt(message: str, password: str): salt = urandom(16) # Generate a random salt key, iv = derive_key_iv(password, salt) backend = default_backend() cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=backend) encryptor = cipher.encryptor() padder = padding.PKCS7(algorithms.AES.block_size).padder() padded_data = padder.update(message.encode()) + padder.finalize() encrypted = encryptor.update(padded_data) + encryptor.finalize() return salt + encrypted # prepend salt for use in decryption def decrypt(token: bytes, password: str): salt = token[:16] # Extract the salt from the beginning encrypted_message = token[16:] key, iv = derive_key_iv(password, salt) backend = default_backend() cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=backend) decryptor = cipher.decryptor() padded_plaintext = decryptor.update(encrypted_message) + decryptor.finalize() unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder() plaintext = unpadder.update(padded_plaintext) + unpadder.finalize() return plaintext.decode()
已知CryptoJS的AES加密逻辑为:从密码派生32字节AES-256密钥和16字节初始化向量(IV),采用CBC模式、PKCS7填充,但调整代码后仍未解决问题,请问如何修改Python代码以正确解密?
解决方案
你的Python代码和CryptoJS的实现存在三处核心不匹配,修正后即可正确解密:
关键差异点
- 密钥派生算法:CryptoJS默认使用PBKDF2而非Scrypt,且固定参数为1000次迭代、SHA-256哈希、8字节salt
- 密文格式:CryptoJS输出的是Base64编码的OpenSSL兼容格式(前缀为
Salted__+ 8字节salt + 密文),而非直接的字节拼接 - 编码一致性:密码和明文需严格使用UTF-8编码,与TypeScript代码中的
Utf8处理匹配
修正后的解密代码
import base64 from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.primitives import padding from cryptography.hazmat.backends import default_backend def decrypt_cryptojs_aes(ciphertext_b64: str, password: str): # 解码Base64密文并拆分格式组件 ciphertext_bytes = base64.b64decode(ciphertext_b64) # 验证CryptoJS密文的OpenSSL格式前缀 if ciphertext_bytes[:8] != b"Salted__": raise ValueError("无效的CryptoJS密文格式") # 提取8字节salt(CryptoJS固定使用8字节salt) salt = ciphertext_bytes[8:16] # 提取实际加密数据 encrypted_data = ciphertext_bytes[16:] # 用PBKDF2派生密钥和IV,严格匹配CryptoJS参数 backend = default_backend() kdf = PBKDF2HMAC( algorithm=hashes.SHA256(), length=48, # 32字节AES-256密钥 + 16字节IV salt=salt, iterations=1000, # CryptoJS默认迭代次数 backend=backend ) key_iv = kdf.derive(password.encode('utf-8')) key = key_iv[:32] iv = key_iv[32:] # CBC模式解密 cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=backend) decryptor = cipher.decryptor() padded_plaintext = decryptor.update(encrypted_data) + decryptor.finalize() # PKCS7去填充 unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder() plaintext = unpadder.update(padded_plaintext) + unpadder.finalize() return plaintext.decode('utf-8')
使用示例
假设TypeScript加密得到的密文为U2FsdGVkX1+M8eK7zL2bN5cR9tY3fH6jK1lO0pQ2sU4dF8gH0jK2lN4mB6vC8xZ0aS2dF4gH6jK8,密码为my-secret-passphrase,调用方式如下:
ciphertext = "U2FsdGVkX1+M8eK7zL2bN5cR9tY3fH6jK1lO0pQ2sU4dF8gH0jK2lN4mB6vC8xZ0aS2dF4gH6jK8" password = "my-secret-passphrase" print(decrypt_cryptojs_aes(ciphertext, password))
内容的提问来源于stack exchange,提问作者Kartik Watwani
相关产品推荐
相关产品推荐

