首页 文章

Node Crypto createHmac()输出是否与具有相同输入的PHP hash_hmac()输出不同?

提问于
浏览
6

我试图在Node中复制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

1 回答

  • 2

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

    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 一样好 .

    为了完整性,Crypto模块支持另一个选项:

    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

相关问题