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

关于将CryptoJS中EvpKDF实现移植到Python的问题

CryptoJS AES加密移植Python:EvpKDF拆分后结果不一致问题

核心问题根源

你遇到的结果不一致,核心原因是CryptoJS中i.toString()的默认行为和你Python中使用的输入格式不匹配:

  • i是CryptoJS的WordArray对象(512位SHA512哈希结果),其默认toString()方法会输出小写十六进制字符串(512位对应128个字符的0-9a-f字符串)。
  • 当你将这个字符串传入CryptoJS.AES.encrypt作为密码参数时,EvpKDF的输入是该十六进制字符串的UTF-8编码字节,而非原始的512位哈希字节。
  • 你拆分EvpKDF单独执行时,大概率直接使用了哈希的原始字节作为输入,导致EvpKDF生成的密钥完全不同。

修复步骤

1. 对齐EvpKDF输入格式

在Python中,按以下流程处理输入:

  • 对原始密码执行1000次SHA512哈希,得到bytes类型的哈希结果。
  • 将哈希结果转换为小写十六进制字符串(和CryptoJS的toString()输出一致)。
  • 将该十六进制字符串按UTF-8编码为字节数组,作为EvpKDF的输入密码。

2. 对齐EvpKDF参数

确保Python实现的EvpKDF和CryptoJS完全一致:

  • 哈希函数:MD5(CryptoJS的EvpKDF默认用MD5)。
  • 迭代次数:1000(和原代码配置一致)。
  • 派生数据长度:需覆盖密钥+IV的总长度。原代码中keySize=128(字节),CryptoJS默认IV长度为16字节(AES-CBC的标准IV长度),所以EvpKDF需派生128+16=144字节数据,前128字节为密钥,后16字节为IV。
  • 盐:如果原代码未指定盐,CryptoJS会自动生成随机盐;若测试时用固定盐,需确保Python中使用完全相同的盐(字节一致)。

3. 对齐AES加密参数

CryptoJS的AES默认配置:

  • 模式:CBC。
  • 填充:Pkcs7。
  • IV:由EvpKDF派生的后16字节数据(若未手动指定)。

代码示例对比

原关键JavaScript逻辑

// 生成1000次SHA512的哈希i
let i = CryptoJS.SHA512("your_password");
for (let j = 0; j < 999; j++) {
    i = CryptoJS.SHA512(i);
}
// 用i的十六进制字符串作为EvpKDF输入,生成128字节密钥+16字节IV
let encrypted = CryptoJS.AES.encrypt("your_plaintext", i.toString(), {
    keySize: 128,
    iterations: 1000
});

对应Python修复逻辑

import hashlib
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad

# 实现CryptoJS风格的EvpKDF(MD5哈希)
def evpkdf(password, salt, key_size, iterations):
    derived = b""
    block = b""
    while len(derived) < key_size:
        block = hashlib.md5(block + password + salt).digest()
        for _ in range(iterations - 1):
            block = hashlib.md5(block).digest()
        derived += block
    return derived[:key_size]

# 步骤1:1000次SHA512哈希
password = b"your_password"
hash_bytes = password
for _ in range(1000):
    hash_bytes = hashlib.sha512(hash_bytes).digest()

# 步骤2:转换为小写十六进制字符串,再转UTF-8字节
kdf_input = hash_bytes.hex().encode("utf-8")

# 步骤3:执行EvpKDF(假设盐和原JS一致,这里用示例盐)
salt = b"your_salt"  # 原JS中若自动生成,需提取对应盐
total_derived_size = 128 + 16  # 密钥128字节 + IV16字节
derived_data = evpkdf(kdf_input, salt, total_derived_size, 1000)
key = derived_data[:128]
iv = derived_data[128:]

# 步骤4:AES加密(对齐CBC+Pkcs7)
cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext = b"your_plaintext"
ciphertext = cipher.encrypt(pad(plaintext, AES.block_size))

验证要点

  • 对比i.toString()的输出和Python中hash_bytes.hex()的结果,确保完全一致(小写十六进制,无额外前缀/后缀)。
  • 对比EvpKDF生成的密钥、IV字节,确保和CryptoJS中派生的结果一致(可通过在JS中打印encrypted.key和encrypted.iv的十六进制值验证)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 04:32:19