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
问题分析
核心差异在于密钥的处理方式:
- CryptoJS中
CryptoJS.HmacSHA256返回的是WordArray类型的二进制哈希对象,当把这个对象作为下一次HMAC的密钥时,会直接使用其原始二进制数据。 - 原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
相关产品推荐
相关产品推荐

