I want to create a hash of I love cupcakes
(si开发者_StackOverflowgned with the key abcdeg
)
How can I create that hash, using Node.js Crypto?
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')
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.
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
Despite all the sample code for signing and verifying hashing algorithms, I still had experiment and tweak quite a bit to make it work. Here's my working sample which I believe has all edge cases covered.
It's URL safe (i.e. doesn't need to be encoded), it takes an expiry time, and will not unexpectedly throw an exception. There is a dependency on Day.js, but you can replace that with another date library or roll your own date comparison.
Written in TypeScript:
// signature.ts
import * as crypto from 'crypto';
import * as dayjs from 'dayjs';
const key = 'some-random-key-1234567890';
const replaceAll = (
str: string,
searchValue: string,
replaceValue: string,
) => str.split(searchValue).join(replaceValue);
const swap = (str: string, input: string, output: string) => {
for (let i = 0; i < input.length; i++)
str = replaceAll(str, input[i], output[i]);
return str;
};
const createBase64Hmac = (message: string, expiresAt: Date) =>
swap(
crypto
.createHmac('sha1', key)
.update(`${expiresAt.getTime()}${message}`)
.digest('hex'),
'+=/', // Used to avoid characters that aren't safe in URLs
'-_,',
);
export const sign = (message: string, expiresAt: Date) =>
`${expiresAt.getTime()}-${createBase64Hmac(message, expiresAt)}`;
export const verify = (message: string, hash: string) => {
const matches = hash.match(/(.+?)-(.+)/);
if (!matches) return false;
const expires = matches[1];
const hmac = matches[2];
if (!/^\d+$/.test(expires)) return false;
const expiresAt = dayjs(parseInt(expires, 10));
if (expiresAt.isBefore(dayjs())) return false;
const expectedHmac = createBase64Hmac(message, expiresAt.toDate());
// Byte lengths must equal, otherwise crypto.timingSafeEqual will throw an exception
if (hmac.length !== expectedHmac.length) return false;
return crypto.timingSafeEqual(
Buffer.from(hmac),
Buffer.from(expectedHmac),
);
};
You can use it like this:
import { sign, verify } from './signature';
const message = 'foo-bar';
const expiresAt = dayjs().add(1, 'day').toDate();
const hash = sign(message, expiresAt);
const result = verify(message, hash);
expect(result).toBe(true);
精彩评论