Hash_hmac equivalent in Node.js

13,394

Solution 1

The primary problem here is that you are using createHash which creates a hash, rather than createHmac which creates an HMAC.

Change createHash to createHmac and you should find it produces the same result.

This is the output you should expect:

chris /tmp/hmac $ cat node.js 
var crypto = require('crypto');
var key = 'abcd';
var data = 'wxyz';

function getHash(string){
    var hmac = crypto.createHmac('sha1', key);
    hmac.update(string); 
    return hmac.digest('binary'); 
};

process.stdout.write(getHash(data));

chris /tmp/hmac $ cat php.php 
<?php
$key = "abcd";
$data = "wxyz";
function __getHash($string)
{
    global $key;
    return hash_hmac('sha1', $string, $key, true); 
}

echo utf8_encode(__getHash($data));

chris /tmp/hmac $ node node.js | base64
WsOKw4xgw4jDlFHDl3jDuEPDuCfCmsOFwoDCrsK/w6ka
chris /tmp/hmac $ php php.php | base64
WsOKw4xgw4jDlFHDl3jDuEPDuCfCmsOFwoDCrsK/w6ka

Solution 2

This answer from Chris is good if you are porting hash_hmac with the last parameter being true. In this case, binary is produced, as is the case with Chris's javascript.

To add to that, this example:

 $sign = hash_hmac('sha512', $post_data, $secret);

Would be ported with a function like so in nodejs:

const crypto = require("crypto");

function signHmacSha512(key, str) {
  let hmac = crypto.createHmac("sha512", key);
  let signed = hmac.update(Buffer.from(str, 'utf-8')).digest("hex");
  return signed
}

The difference here being that when you leave off the last argument to hash_hmac (or set it to something not true), it behaves as defined in the PHP docs:

When set to TRUE, outputs raw binary data. FALSE outputs lowercase hexits.

In order to do this with node.js we use digest('hex') as you can see in the snippet.

Share:
13,394
Sara Fuerst
Author by

Sara Fuerst

I am the Founder and Lead Developer of Tibsar Software LLC. I have always had a passion for software development. Today I use this passion to help aspiring business owners build their software based business. I focus on creating quality software products that can scale with their business and meet their unique needs.

Updated on June 11, 2022

Comments

  • Sara Fuerst
    Sara Fuerst almost 2 years

    I have code that is working in my PHP app. In the PHP I sign the url with the following code:

    private static function __getHash($string)
    {
        return hash_hmac('sha1', $string, self::$__secretKey, true);    
    }
    

    I am attempting to sign the URL in the same way in a Node.js application. This is what I'm trying:

    S3.prototype.getHash = function(string){
        var key = this.secret_key; 
        var hmac = crypto.createHash('sha1', key);
        hmac.update(string); 
        return hmac.digest('binary'); 
    }; 
    

    However, I am getting the following error:

    The request signature we calculated does not match the signature you provided. Check your key and signing method.

    Do these pieces of code do the same thing? Am I missing something?

  • Sara Fuerst
    Sara Fuerst about 8 years
    I'm still getting that the Signature does not match. Any idea why?
  • Chris
    Chris about 8 years
    Well how are you comparing the binary data? If you compare them as strings you will need to make sure the encoding is correct. I've attached the output I get. Note I've converted the PHP output from latin-1 to UTF-8 and base64 encoded both outputs for easy comparison.
  • keyvan
    keyvan almost 7 years
    Sara: hash_hmac produces a hex digest by default