213

I want to create a hash of I love cupcakes (signed with the key abcdeg)

How can I create that hash, using Node.js Crypto?

Sebastian Simon
  • 14,320
  • 6
  • 42
  • 61
user847495
  • 8,403
  • 15
  • 42
  • 47

3 Answers3

383

Documentation for crypto: http://nodejs.org/api/crypto.html

const crypto = require('crypto')

const text = 'I love cupcakes'
const key = 'abcdeg'

crypto.createHmac('sha1', key)
  .update(text)
  .digest('hex')
Ricardo Tomasi
  • 31,690
  • 2
  • 52
  • 65
  • 'hex' is not always needed, for example for doing the hmac digest equivalent of ruby. – htafoya Sep 20 '18 at 09:46
  • 8
    And to verify a hash, you should use `crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b))`: https://stackoverflow.com/questions/31095905/whats-the-difference-between-a-secure-compare-and-a-simple/31096242#comment97670146_31096242 – baptx Aug 02 '19 at 15:19
  • 1
    The circle is complete: https://nodejs.org/api/crypto.html#crypto_crypto – Ricardo Tomasi Dec 02 '19 at 17:33
100

A few years ago it was said that update() and digest() were legacy methods and the new streaming API approach was introduced. Now the docs say that either method can be used. For example:

var crypto    = require('crypto');
var text      = 'I love cupcakes';
var secret    = 'abcdeg'; //make this your secret!!
var algorithm = 'sha1';   //consider using sha256
var hash, hmac;

// Method 1 - Writing to a stream
hmac = crypto.createHmac(algorithm, secret);    
hmac.write(text); // write in to the stream
hmac.end();       // can't read from the stream until you call end()
hash = hmac.read().toString('hex');    // read out hmac digest
console.log("Method 1: ", hash);

// Method 2 - Using update and digest:
hmac = crypto.createHmac(algorithm, secret);
hmac.update(text);
hash = hmac.digest('hex');
console.log("Method 2: ", hash);

Tested on node v6.2.2 and v7.7.2

See https://nodejs.org/api/crypto.html#crypto_class_hmac. Gives more examples for using the streaming approach.

Adam Griffiths
  • 1,486
  • 1
  • 12
  • 10
  • Not a one-liner, and calls can't be daisy-chained... but I'll use this approach. – tim-montague Jul 26 '14 at 21:43
  • 2
    I can't, for the life of me, make this work. hmac.read() returns a " [object SlowBuffer]" and if I try to read the contents using hmac.read().toString('hex'); I do not get the expected value. If I use the update/digest deprecated approach, it returns the expected string. I'm using this to validate a signature from a third party POST to my servers. Any ideas what is going on? – AngraX Aug 25 '14 at 17:03
  • Perhaps the hmac.read is happening before the data has been flushed to the stream? Maybe hmac.read should be driven by the stream's finish event? – Dave Sep 30 '14 at 04:22
  • Actually the link that you posted explicitely mentions the use of `update` and not `write`. I am confused, which is best practice now? I cant find resources that tell that as clearly as you mention it. – SCBuergel Jul 30 '16 at 21:02
  • 5
    As of Nov. 2016, `digest` and `update` have *not* been deprecated and are featured in the documentation: https://nodejs.org/api/crypto.html#crypto_class_hmac. I recommend using the stream API only if you're reading from a stream. – Ricardo Tomasi Nov 15 '16 at 19:41
  • It's true, `update` and `digest` were not depreciated. When I originally answered this question in 2013 the docs said they were "legacy methods" but they were changed in 2015 to say using piped streams (as in my answer) or using `update` and `digest` (as in Ricardo's answer) are both valid methods. [Here](https://github.com/nodejs/node/commit/cc82e5e3d9aefb5e31dbe688b0038327bb7d9b80?diff=unified#commitcomment-21891754) is where the docs were changed and [here](https://github.com/nodejs/node/blob/master/doc/api/crypto.md#class-hash) are the current docs. – Adam Griffiths Apr 25 '17 at 07:24
  • @AdamGriffiths works good both of them. What you recommend to use? – Raz Feb 21 '18 at 16:20
22

Gwerder's solution wont work because hash = hmac.read(); happens before the stream is done being finalized. Thus AngraX's issues. Also the hmac.write statement is un-necessary in this example.

Instead do this:

var crypto    = require('crypto');
var hmac;
var algorithm = 'sha1';
var key       = 'abcdeg';
var text      = 'I love cupcakes';
var hash;

hmac = crypto.createHmac(algorithm, key);

// readout format:
hmac.setEncoding('hex');
//or also commonly: hmac.setEncoding('base64');

// callback is attached as listener to stream's finish event:
hmac.end(text, function () {
    hash = hmac.read();
    //...do something with the hash...
});

More formally, if you wish, the line

hmac.end(text, function () {

could be written

hmac.end(text, 'utf8', function () {

because in this example text is a utf string

Dave
  • 637
  • 6
  • 12
  • You are wrong, there is no need for adding a callback. This stream is synchronous and is readable right after end() is called. Most fascinating thing is it is written in official documentation, but everyone have to put their 5 (bent) cents in. – stroncium Nov 02 '15 at 04:39
  • Are you trolling? Perhaps you should read the documentation. If you try to read the stream before the finish event, it will fail. – Dave Nov 03 '15 at 06:30
  • 1
    From [https://nodejs.org/api/crypto.html#crypto_class_hmac] `It is a stream that is both readable and writable. The written data is used to compute the hmac. Once the writable side of the stream is ended, use the read() method to get the computed digest.` You read it when **writable** side **ended**, you don't need to even wait for when _readable_ side becomes _readable_ (though it surely does). Read your documentation please. – stroncium Nov 03 '15 at 14:29
  • createHmac creates a **stream**. "_ended_" in the documentation line you quote above does not mean `hmac.end(...)` has been called, "_ended_" means that the stream has _raised its finish event_, which is why the command accepts a callback. After the end() method is called, the stream requires time to flush the data to the underlying system. If you call read() before the finish event is raised, it will fail. Go ahead and paster Gwerder's code into JSbin and see for yourself. You should be reading the _Streams_ documentation to understand how it works. – Dave Nov 04 '15 at 07:02
  • I've used it in production code for some time and it is stable as hell. I honestly don't know what JSBin is, but I've also tried the supported code in nodejs with just copy-paste and it works too. You shouldn't imagine additional meanings to documentation. "ended" always means "ended" everywhere in documentation. Yet again, you seem to misunderstand that stream have 2 sides. And in documentation it is explicitly stated that person can use `read()` when **writable** side ended, and there is nothing about finish event. – stroncium Nov 04 '15 at 18:49
  • And even some abstract solution which reads hmacs from all over the world should not depend on _writable_ `finish` event, but on _readable_ `readable` and `end` events. – stroncium Nov 04 '15 at 18:53
  • By your logic it is ok to read data when some system decides that it have done flushing all the data required to generate it, but before actual data producer generated anything. So how about not introducing extremely leaky abstractions and callback hell when we have a system(which is actually purely synchronous) which is guaranteed to work in a simple, effective and predictable way? – stroncium Nov 04 '15 at 19:02