首页 > 解决方案 > Node Crypto createHmac() 输出是否与具有相同输入的 PHP hash_hmac() 输出不同?

问题描述

我正在尝试在 Node.js 中复制 PHP 哈希生成函数。此哈希用作 API 的一部分。PHP 版本会创建系统接受的正确输出。尽管我认为函数的输入相同,但 Node 版本会创建不同的输出。

这是因为 PHP 和 Node HMAC 函数有一些根本不同的工作方式吗?还是因为我只是缺少字符编码的一些怪癖?还是我只是把别的东西搞砸了?


PHP 代码

$url = 'https://example.com/api/endpoint';
$user = 'apiuser';

// Example key
$key = '+raC8YR2F+fZypNJ5q+CAlqLFqNN1AlAfWwkwJLcI7jrAvppjRPikWp523G/u0BLSpN9+2LusJvpSwrfU9X2uA==';

$timestamp = gmdate('D, d M Y H:i:s T', 1543554184); // gmdate('D, d M Y H:i:s T');

$hashdata = "GET\n$url\n$user\n$timestamp\n";

print_r($hashdata);
/*
GET
https://example.com/api/endpoint
apiuser
Fri, 30 Nov 2018 05:03:04 GMT
*/

$decoded_key = base64_decode($key);

print_r(unpack('H*', $decoded_key));
// Array ( [1] => fab682f1847617e7d9ca9349e6af82025a8b16a34dd409407d6c24c092dc23b8eb02fa698d13e2916a79db71bfbb404b4a937dfb62eeb09be94b0adf53d5f6b8 )

$generated_hash = hash_hmac('sha256', $hashdata, $decoded_key, true);

$encoded_hash = base64_encode($generated_hash);

print_r($encoded_hash);
// vwdT8XhtSA1q+JvAfsRpJumfI4pemoaNFbjjc5JFsvw=

Node.js 代码

crypto = require('crypto');
moment = require('moment-timezone');

let url = 'https://example.com/api/endpoint';
let api_user = 'apiuser';

// Example key
let api_key = '+raC8YR2F+fZypNJ5q+CAlqLFqNN1AlAfWwkwJLcI7jrAvppjRPikWp523G/u0BLSpN9+2LusJvpSwrfU9X2uA==';

let timestamp = moment.tz(1543554184 * 1000, 'GMT').format('ddd, DD MMM YYYY HH:mm:ss z'); // moment.tz(new Date(), 'GMT').format('ddd, DD MMM YYYY HH:mm:ss z');

let hash_data = 'GET\n' + url + '\n' + api_user + '\n' + timestamp + '\n';

console.log($hashdata);
/*
GET
https://example.com/api/endpoint
apiuser
Fri, 30 Nov 2018 05:03:04 GMT
*/

let decoded_key = Buffer.from(api_key, 'base64').toString('utf8');

console.log(Buffer.from(api_key, 'base64'));
// <Buffer fa b6 82 f1 84 76 17 e7 d9 ca 93 49 e6 af 82 02 5a 8b 16 a3 4d d4 09 40 7d 6c 24 c0 92 dc 23 b8 eb 02 fa 69 8d 13 e2 91 6a 79 db 71 bf bb 40 4b 4a 93 ... >

const hmac = crypto.createHmac('sha256', decoded_key);
hmac.update(hash_data);

// Not sure which should be closest to PHP
// Or if there is a difference
let encoded_hash = hmac.digest('base64');
// let encoded_hash = Buffer(hmac.digest('binary')).toString('base64');

console.log(encoded_hash);
// hmac.digest('base64') == eLLVC9cUvq6Ber6t9TBTihSoq+2VWIMUJKiL4/fIj3s=
// Buffer(hmac.digest('binary')).toString('base64') == eLLVC9cUvq6Ber6t9TBTihSoq+2VWIMUJKiL4/fIj3s=

除了 HMAC 函数输出之外的所有内容似乎都相同。

操作系统:Windows 10 - 64 位

Node.js 版本:v10.13.0

PHP版本:7.2.7

标签: phpnode.js

解决方案


decoded_key我可以通过保留aBuffer并将其直接作为 a 发送Buffer到来在 Node.js 中获得正确的结果crypto.createHmac

let decoded_key = Buffer.from(api_key, 'base64');
const hmac = crypto.createHmac('sha256', decoded_key);

这是受支持的,请参阅crypto.createHmac

key <string> | <Buffer> | <TypedArray> | <DataView>

结果是vwdT8XhtSA1q+JvAfsRpJumfI4pemoaNFbjjc5JFsvw=- 与 PHP 相同。
工作示例:https ://repl.it/repls/DisguisedBlankTechnologies

问题一定出在.toString('utf8'). 我没有找到另一种将作品编码为字符串的方法,但它的作用与Buffer.

为了完整起见,加密模块支持的另一个选项:

const hmac = crypto.createHmac('sha256', decoded_key);
hmac.write(hash_data);
hmac.end();
let encoded_hash = hmac.read().toString('base64');

工作示例:https ://repl.it/repls/LightcoralUnwelcomeProfessionals


推荐阅读