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

CryptoJS与Python的HMACSHA256输出差异排查求助

CryptoJS转Python HMACSHA256签名不一致问题解决

问题背景

现有一套可正常运行的CryptoJS签名逻辑,转换为Python代码做测试用例时,相同参数下使用HMACSHA256算法,首次哈希计算结果一致,但第二次计算结果出现差异。

CryptoJS代码

<html>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
<script>
window.onload = function(){

    var privateKey = "F2FD99BA2BA7335BF0336CA0A5714F02042D073D3742A312";
    var targetSystem = "mydomain.de";
    
    var preDerivedKey = GetPreDerivedKey(privateKey, targetSystem, CurrentUtcDateTimeStamp());
}   

function CurrentUtcDateTimeStamp()
{
    // For debugging, set to 0 for now
    return "0";
};

function Encrypt(data, password)
{
    var hash = CryptoJS.HmacSHA256(data, password);
    
    console.log("Function Encrypt (using data '" + data + "' and password '" + password + "'), Hash is: " + hash);
    return hash;
};

function GetPreDerivedKey(privateKey, targetSystem, timestamp)
{
    console.log("Function GetPreDerivedKey received: privateKey '" +  privateKey + "', targetSystem '" + targetSystem + "', timestamp '" + timestamp + "'");
    var derivedKey = Encrypt(targetSystem, privateKey);
    derivedKey = Encrypt(timestamp, derivedKey);
    return derivedKey;
};
</script>
</html>

Python代码(原错误版本)

import hashlib
import hmac

privateKey = "F2FD99BA2BA7335BF0336CA0A5714F02042D073D3742A312"
targetSystem = "mydomain.de"

def CurrentUtcDateTimeStamp():
    return "0"

def Encrypt(data, password):
    hash = hmac.new(password.encode('utf-8'),msg=data.encode('utf-8'),digestmod=hashlib.sha256).hexdigest()
    
    print("Function Encrypt (using data '" + data + "' and password '" + password + "'), Hash is:")
    
    hexstring = ""
    for bytes in hash:
        hexstring += str(bytes)
    print(hexstring)

    return hash

def GetPreDerivedKey(privateKey, targetSystem, timestamp):
    print("Function GetPreDerivedKey received: privateKey '" +  privateKey + "', targetSystem '" + targetSystem + "', timestamp '" + timestamp + "'")
    derivedKey = Encrypt(targetSystem, privateKey)
    derivedKey = Encrypt(timestamp, derivedKey)
    return derivedKey

# Signing
preDerivedKey = GetPreDerivedKey(privateKey, targetSystem, CurrentUtcDateTimeStamp());

执行输出对比

Python输出

Function GetPreDerivedKey received: privateKey 'F2FD99BA2BA7335BF0336CA0A5714F02042D073D3742A312', targetSystem 'mydomain.de', timestamp '0'
Function Encrypt (using data 'mydomain.de' and password 'F2FD99BA2BA7335BF0336CA0A5714F02042D073D3742A312'), Hash is:
3c0f696075960ec05e0b0ace52594cd49855aff052489911608c6b9ea37a553e
Function Encrypt (using data '0' and password '3c0f696075960ec05e0b0ace52594cd49855aff052489911608c6b9ea37a553e'), Hash is:
48e5d0f3f6ac4563b765ff0caa9b09ec34cddbbc247185d50785a2b1e8b6eb59

CryptoJS输出

Function GetPreDerivedKey received: privateKey 'F2FD99BA2BA7335BF0336CA0A5714F02042D073D3742A312', targetSystem 'mydomain.de', timestamp '0' 
Function Encrypt (using data 'mydomain.de' and password 'F2FD99BA2BA7335BF0336CA0A5714F02042D073D3742A312'), Hash is:
3c0f696075960ec05e0b0ace52594cd49855aff052489911608c6b9ea37a553e
Function Encrypt (using data '0' and password '3c0f696075960ec05e0b0ace52594cd49855aff052489911608c6b9ea37a553e'), Hash is:
577ae299e95e289395c4614412b60dc527bdba2066ccd3e76af0e4cbb4cc4495

第二次哈希结果差异:

  • Python结果:48e5d0f3f6ac4563b765ff0caa9b09ec34cddbbc247185d50785a2b1e8b6eb59
  • CryptoJS结果:577ae299e95e289395c4614412b60dc527bdba2066ccd3e76af0e4cbb4cc4495

问题分析

核心差异在于密钥的处理方式:

  1. CryptoJS中CryptoJS.HmacSHA256返回的是WordArray类型的二进制哈希对象,当把这个对象作为下一次HMAC的密钥时,会直接使用其原始二进制数据。
  2. 原Python代码中,Encrypt函数返回的是哈希的十六进制字符串,第二次调用时直接将该字符串按UTF-8编码转换为字节作为密钥。这相当于把十六进制字符(如3、c、0等)的ASCII码字节作为密钥,而非第一次哈希的原始二进制值,导致密钥不一致,最终哈希结果不同。

解决方案

修改Python代码,确保第二次调用时使用第一次哈希的原始二进制数据作为密钥。以下两种方式均可实现:

方式1:修改Encrypt函数,同时返回十六进制结果和二进制摘要

import hashlib
import hmac

privateKey = "F2FD99BA2BA7335BF0336CA0A5714F02042D073D3742A312"
targetSystem = "mydomain.de"

def CurrentUtcDateTimeStamp():
    return "0"

def Encrypt(data, password):
    # 处理密钥:如果是bytes类型直接使用,否则按UTF-8编码转换
    if isinstance(password, bytes):
        key = password
    else:
        key = password.encode('utf-8')
    
    # 计算HMAC-SHA256,同时获取二进制摘要和十六进制字符串
    hmac_obj = hmac.new(key, msg=data.encode('utf-8'), digestmod=hashlib.sha256)
    digest_hex = hmac_obj.hexdigest()
    
    print(f"Function Encrypt (using data '{data}' and password '{password}'), Hash is:")
    print(digest_hex)
    
    return digest_hex, hmac_obj.digest()

def GetPreDerivedKey(privateKey, targetSystem, timestamp):
    print(f"Function GetPreDerivedKey received: privateKey '{privateKey}', targetSystem '{targetSystem}', timestamp '{timestamp}'")
    # 第一次加密,获取十六进制结果和二进制摘要
    derivedKeyHex, derivedKeyBytes = Encrypt(targetSystem, privateKey)
    # 第二次加密,使用第一次的二进制摘要作为密钥
    derivedKeyHex, _ = Encrypt(timestamp, derivedKeyBytes)
    return derivedKeyHex

# Signing
preDerivedKey = GetPreDerivedKey(privateKey, targetSystem, CurrentUtcDateTimeStamp())

方式2:在第二次调用时将十六进制字符串转回二进制

import hashlib
import hmac

privateKey = "F2FD99BA2BA7335BF0336CA0A5714F02042D073D3742A312"
targetSystem = "mydomain.de"

def CurrentUtcDateTimeStamp():
    return "0"

def Encrypt(data, password):
    # 处理密钥:如果是bytes类型直接使用,否则按UTF-8编码转换
    if isinstance(password, bytes):
        key = password
    else:
        key = password.encode('utf-8')
    
    digest_hex = hmac.new(key, msg=data.encode('utf-8'), digestmod=hashlib.sha256).hexdigest()
    
    print(f"Function Encrypt (using data '{data}' and password '{password}'), Hash is:")
    print(digest_hex)
    
    return digest_hex

def GetPreDerivedKey(privateKey, targetSystem, timestamp):
    print(f"Function GetPreDerivedKey received: privateKey '{privateKey}', targetSystem '{targetSystem}', timestamp '{timestamp}'")
    derivedKeyHex = Encrypt(targetSystem, privateKey)
    # 将十六进制字符串转回二进制字节,作为下一次的密钥
    derivedKeyBytes = bytes.fromhex(derivedKeyHex)
    derivedKeyHex = Encrypt(timestamp, derivedKeyBytes)
    return derivedKeyHex

# Signing
preDerivedKey = GetPreDerivedKey(privateKey, targetSystem, CurrentUtcDateTimeStamp())

修改后,Python的第二次哈希结果将与CryptoJS完全一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 20:19:01