Crypto.js与pycrypto的AES加密结果不一致,如何实现跨语言兼容?
AES跨语言(CryptoJS vs pycrypto)兼容问题解决
问题背景
在Node.js环境使用CryptoJS的AES算法加密文本,JS内加解密正常;Python用pycrypto实现相同逻辑,Python内加解密也正常,但JS生成的密文无法在Python解密,核心原因是双方加密结果不一致。
JS侧实现代码
import CryptoJS from "crypto-js"; let str = "lol"; let salt = "ABCDEFGHIJKLMNOP"; let key = "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"; salt = CryptoJS.enc.Hex.parse(salt); key = CryptoJS.enc.Hex.parse(key); const options = { iv: salt, padding: CryptoJS.pad.ZeroPadding, mode: CryptoJS.mode.CFB, }; // 加密函数 function encrypt(str, salt, key) { const cipher = CryptoJS.AES.encrypt(str, key, options); console.log("cipher", CryptoJS.enc.Hex.stringify(cipher.ciphertext)); return cipher.ciphertext.toString(CryptoJS.enc.Base64); } // 解密函数 function decrypt(str, salt, key) { const cipher = CryptoJS.AES.decrypt(str, key, options); return cipher.toString(CryptoJS.enc.Utf8); } const encrypted = encrypt(str, salt, key); console.log("encrypted", encrypted); const decrypted = decrypt(encrypted, salt, key); console.log("decrypted", decrypted); // 输出 // encrypted sd4Xpz/ws8x2j+cgF17t6A== // decrypted lol
Python侧原实现代码
from Crypto.Cipher import AES from base64 import b64encode, b64decode str = "lol"; salt = b"ABCDEFGHIJKLMNOP"; key = "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"; enc_dec_method = 'utf-8' def encrypt(str_to_enc, str_key, salt): aes_obj = AES.new(str_key.encode('utf-8'), AES.MODE_CFB, salt) hx_enc = aes_obj.encrypt(str_to_enc.encode('utf8')) mret = b64encode(hx_enc).decode(enc_dec_method) return mret def decrypt(str_to_dec, str_key, salt): aes_obj = AES.new(str_key.encode('utf-8'), AES.MODE_CFB, salt) str_tmp = b64decode(str_to_dec.encode(enc_dec_method)) str_dec = aes_obj.decrypt(str_tmp) mret = str_dec.decode(enc_dec_method) return mret test_enc_text = encrypt(str, key, salt) test_dec_text = decrypt(test_enc_text, key, salt) print(f"Encrypted Text: {test_enc_text}") print(f"Decrypted Text: {test_dec_text}") # 输出 # Encrypted Text: o9XB # Decrypted Text: lol
已尝试的排查步骤
- 分别验证两种语言内的加解密功能正常
- 查阅两个库的官方文档与源码
- 排查编码/解码环节问题,未找到原因
关键差异点与解决方案
1. 密钥与IV的解析方式不一致
JS侧使用CryptoJS.enc.Hex.parse()将字符串解析为十六进制字节,而Python侧直接将字符串按UTF-8编码为字节,导致双方实际使用的密钥、IV二进制数据完全不同。
- 修正:Python侧需用
binascii.unhexlify()将原字符串按十六进制解析为字节,和JS侧对齐。
2. CFB模式的段大小(Segment Size)不同
CryptoJS的CFB模式默认使用128位段大小(与AES块大小一致),而pycrypto的AES.MODE_CFB默认使用8位段大小,这是加密结果差异的核心原因。
- 修正:Python初始化AES对象时,指定
segment_size=128,和CryptoJS对齐。
3. 填充方式的冗余设置
CFB模式属于流密码模式,本身不需要填充,JS侧设置的ZeroPadding不会生效(因为CryptoJS在流模式下会忽略填充配置),Python侧也无需额外设置填充,这点不影响当前场景,但需明确流模式的特性。
修正后的Python代码
from Crypto.Cipher import AES from base64 import b64encode, b64decode import binascii str = "lol" # 按十六进制解析salt,与JS侧CryptoJS.enc.Hex.parse()对齐 salt = binascii.unhexlify("ABCDEFGHIJKLMNOP") # 按十六进制解析key,与JS侧对齐 key = binascii.unhexlify("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP") enc_dec_method = 'utf-8' def encrypt(str_to_enc, str_key, salt): # 指定CFB段大小为128位,匹配CryptoJS默认值 aes_obj = AES.new(str_key, AES.MODE_CFB, salt, segment_size=128) hx_enc = aes_obj.encrypt(str_to_enc.encode(enc_dec_method)) mret = b64encode(hx_enc).decode(enc_dec_method) return mret def decrypt(str_to_dec, str_key, salt): aes_obj = AES.new(str_key, AES.MODE_CFB, salt, segment_size=128) str_tmp = b64decode(str_to_dec.encode(enc_dec_method)) str_dec = aes_obj.decrypt(str_tmp) mret = str_dec.decode(enc_dec_method) return mret test_enc_text = encrypt(str, key, salt) test_dec_text = decrypt(test_enc_text, key, salt) print(f"Encrypted Text: {test_enc_text}") print(f"Decrypted Text: {test_dec_text}")
修正后,Python生成的密文与JS侧一致:sd4Xpz/ws8x2j+cgF17t6A==,且能正常解密JS生成的密文。
内容的提问来源于stack exchange,提问作者Gaurav Rai
相关产品推荐
相关产品推荐

