当 unicode 字符时,NodeJS 中的加密失败

Crypto in NodeJS fails when unicode characters

我正在尝试使用 NodeJS Crypto 库验证数据完整性。它需要计算一个JSON字符串的Hmac。

经过一些测试,我找到了问题所在:只要它包含一个 unicode 字符,它就会发生。例如:

var hasher = crypto.createHmac("sha256", 'secret_key');
hasher.write('{"timezone":"(GMT-05:00) Eastern Time (US \u0026 Canada)"}');
hasher.end();

var calculatedHmac = new Buffer(hasher.read(), 'utf8').toString('base64');
console.log(calculatedHmac);

然而这个returns 错误的hmac。 PHP 中的相同代码与我从第三方服务收到的签名匹配:

$data = '{"timezone":"(GMT-05:00) Eastern Time (US \u0026 Canada)"}';

$calculated_hmac = base64_encode(hash_hmac('sha256', $data, 'secret_key', true));
var_dump($calculated_hmac); // Result is correct here

如果我删除 NodeJS 负载中的“\u0026”,那么我会得到与 PHP.

中相同的正确结果

我是不是做错了什么?

谢谢!

我不是 PHP 专家,但我有一个很好的猜测:

您一定遇到了字符集问题。您在 node.js 中明确地将缓冲区转换为 UTF-8。通过这种方式,您可以从 UTF-8 编码字符串的二进制表示中获得哈希值。

我敢打赌 PHP 在内部处理另一个字符集(可能是 Unicode),它将为您的字符串提供不同的二进制表示形式。

为了正确处理,您必须确保在散列之前将两者转换为相同的二进制表示形式。

更新:我刚刚注意到您直接对字符串进行哈希处理并将哈希表示形式转换为 UTF8。您必须反转此行为:在散列之前将您的字符串转换为 UTF-8。您必须在 PHP 中进行相同的操作。两个平台必须在散列之前同意编码。

只有版本 7 (reference) 的 PHP 支持 \uXXXX 转义序列符号。

如果您使用的是较旧的 PHP 版本,这些转义序列将按字面意义传递(因此 \u0026 是一个 6 字符的子字符串),而 Node 会将其解释为单个字符(&).

如果你想让Node停止解释转义序列,你需要转义反斜杠:

hasher.write('{"timezone":"(GMT-05:00) Eastern Time (US \u0026 Canada)"}');

当您这样做时,Node 和 PHP 的结果将是相同的(确切地说是 0CE0++Kn9mi5xd7nAz/mWOrr7939RWwzfxhBzxAWtAk=)。