使用CryptoJS解密Python Cryptodome AES加密数据时遇UTF-8错误
问题:Angular中使用CryptoJS解密Python Cryptodome加密内容时出现「Malformed UTF-8 data」错误
我尝试在Angular中用CryptoJS解密Python Cryptodome加密的消息,已编写Python加密函数和Angular解密服务,但解密时触发「ERROR Error: Malformed UTF-8 data」错误。我知道是转UTF-8时出现非法字符,但找不到解决办法,恳请提供解决方案。
Python加密代码
import os from Crypto.Cipher import AES from Crypto.Util import padding import base64 def encrypt_aes_256_cbc(data): key = os.getenv('ENCRYPT_KEY') iv = os.getenv('ENCRYPT_IV') # 确保密钥和IV为字节类型 key = bytes.fromhex(key) iv = bytes.fromhex(iv) # 将数据转为字节 data_bytes = data.encode('utf-8') padded_data = _pkcs7_pad(data_bytes, AES.block_size) # 创建AES-256 CBC模式的密码器 cipher = AES.new(key, AES.MODE_CBC, iv) # 加密数据 ciphertext = cipher.encrypt(padded_data) # 拼接IV和密文后做Base64编码 combined_data = iv + ciphertext encoded_ciphertext = base64.b64encode(combined_data).decode("utf-8") return encoded_ciphertext def _pkcs7_pad(data, block_size): padder = padding.PKCS7(block_size).padder() padded_data = padder.update(data) + padder.finalize() print('padded data =', padded_data) return padded_data
Angular解密代码(原错误版本)
export class EncryptDecryptService { iv = CryptoJS.enc.Hex.parse(environment.iv); decryptKey = CryptoJS.enc.Hex.parse(environment.keyDecryptage); decryptData(encrypted_json_string: string) { // const or = CryptoJS.enc.Base64.parse(encrypted_json_string).toString(CryptoJS.enc.Utf8) const decrypted = CryptoJS.AES.decrypt( encrypted_json_string, this.decryptKey, { iv: this.iv } ); console.log("decrypted.toString(CryptoJS.enc.Latin1) =", decrypted.toString(CryptoJS.enc.Utf8)); return decrypted.toString(CryptoJS.enc.Utf8); } }
解决方案
问题核心是CryptoJS解密参数格式与Python加密输出不匹配:
Python端把IV + 密文拼接后做了Base64编码,但CryptoJS的AES.decrypt方法直接接收Base64字符串时,默认会按OpenSSL格式或CipherParams JSON解析,而非我们需要的「IV+密文」组合格式,导致解密出乱码,转UTF-8时报错。
修改后的Angular解密代码:
export class EncryptDecryptService { decryptKey = CryptoJS.enc.Hex.parse(environment.keyDecryptage); decryptData(encrypted_json_string: string) { // 1. 将Base64编码的IV+密文解析为WordArray const combinedData = CryptoJS.enc.Base64.parse(encrypted_json_string); // 2. 拆分IV(AES块大小为16字节,对应4个Word,每个Word占4字节)和密文 const iv = CryptoJS.lib.WordArray.create(combinedData.words.slice(0, 4)); const ciphertext = CryptoJS.lib.WordArray.create(combinedData.words.slice(4)); // 3. 传入CipherParams对象解密,显式指定PKCS7填充(与Python端对齐) const decrypted = CryptoJS.AES.decrypt( { ciphertext: ciphertext }, this.decryptKey, { iv: iv, padding: CryptoJS.pad.Pkcs7 } ); // 4. 转换为UTF-8字符串返回 return decrypted.toString(CryptoJS.enc.Utf8); } }
额外注意事项
- 确保Python和Angular使用的密钥、IV完全一致:密钥需为32字节(对应64位十六进制字符串),IV为16字节(对应32位十六进制字符串)
- 两端填充方式均为PKCS7,CryptoJS默认使用该填充,但显式指定可避免潜在兼容性问题
- 不要直接将Base64字符串传给
AES.decrypt的第一个参数,需拆分为包含ciphertext字段的CipherParams对象
内容的提问来源于stack exchange,提问作者girl of data
相关产品推荐
相关产品推荐

