简体   繁体   English

如何使用 Node.js Crypto 创建 HMAC-SHA1 哈希?

[英]How do I use Node.js Crypto to create a HMAC-SHA1 hash?

I want to create a hash of I love cupcakes (signed with the key abcdeg )我想创建一个I love cupcakes的哈希(用密钥abcdeg

How can I create that hash, using Node.js Crypto?如何使用 Node.js Crypto 创建该哈希?

Documentation for crypto: http://nodejs.org/api/crypto.html加密文档: 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.几年前有人说update()digest()是遗留方法,并且引入了新的流 API 方法。 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在节点 v6.2.2 和 v7.7.2 上测试

See https://nodejs.org/api/crypto.html#crypto_class_hmac .请参阅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(); Gwerder 的解决方案不起作用,因为hash = hmac.read(); happens before the stream is done being finalized.在流完成之前发生。 Thus AngraX's issues.因此 AngraX 的问题。 Also the hmac.write statement is un-necessary in this example.在本例中hmac.write需要hmac.write语句。

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因为在这个例子中 text 是一个 utf 字符串

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 (ie doesn't need to be encoded), it takes an expiry time, and will not unexpectedly throw an exception.它是 URL 安全的(即不需要编码),它需要一个到期时间,并且不会意外抛出异常。 There is a dependency on Day.js , but you can replace that with another date library or roll your own date comparison.有对Day.js的依赖,但您可以将其替换为另一个日期库或滚动您自己的日期比较。

Written in TypeScript:用 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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM