php - 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
解决方案
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
推荐阅读
- try-with-resources - CachedRowSet 总是返回空
- ios - 如何通过丰富的通知操作将数据发布到 Firestore?
- java - 用于重试注释的 Bean 后处理器
- java - 通用图像加载器太慢
- asp.net-mvc - 我正在尝试在 asp.net 中播种用户和用户角色,但只有角色成功
- python - 获取图片RGB值所需时间
- php - CodeIgniter 一查询多语句
- reactjs - 反应将图像添加到现有地图
- javascript - 如何动态创建以下 htm 代码
- django-rest-framework - 使用序列化程序(drf)在 django 中进行密码加密