加密功能模块

crypto 模块现在是 node@10 中的一个内置模块了,其提供了加密功能,包括对 OpenSSL 的哈希、HMAC、加密、解密、签名、以及验证功能的一整套封装。

代码中引用如下:

const crypto = require('crypto');
1

还可以检测当前环境是否支持 crypto:

let crypto ;
try {
    crypto = require('crypto');
} catch (err) {
    console.error('不支持 crypto');
}
1
2
3
4
5
6

Hash 类

Hash 类是用于创建数据哈希值的工具类,其包含的算法有:

  • MD4
  • MD5
  • SHA1
  • SHA256
  • ...

crypto.createHash(algorithm) 所支持的算法如下可以通过终端命令查看下。

可以在终端上查看支持的算法:

# 新版本 OpenSSL 的命令
$ openssl list -digest-algorithms

# 旧版本 OpenSSL 的命令
$ openssl list-message-digest-algorithms
1
2
3
4
5

其可如下使用:

  • 使用其作为 的特性,既可读又可写;
  • 使用 hash.update()hash.digest() 方法产生计算后的哈希。

方法 1:流式读写计算目标文本的哈希值

单次与多次读写:

const crypto = require('crypto');

class MySHA256Cryptor {
    constructor () {
        this.result = '';
        this.hasher = null;
        this.init();
    }

    init () {
        this.result = '';
        this.hasher = crypto.createHash('sha256');
        this.hasher.on('readable', () => {
            // 哈希流只会生成一个元素。
            const data = this.hasher.read();

            if (data) {
                this.result = data.toString('hex');
            } else {
                this.result = '';
            }
        });
    }

    updateStr (str) {
        this.hasher.write(str);
        return this;
    }

    getResult () {
        this.hasher.end()
        return this.result;
    }
}

console.log(
    (new MySHA256Cryptor()).updateStr('ILOVEYOU').getResult()
);

console.log(
    (new MySHA256Cryptor())
        .updateStr('I')
        .updateStr('LOVE')
        .getResult()
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

管道流读写:

const fs = require('fs');
const stream = require('stream');
const crypto = require('crypto');
const hasher = crypto.createHash('sha256');

const fsStream = fs.createReadStream('./demos/file.txt');
stream.pipe(hasher);

hasher.on('readable', () => {
    // 一些处理操作
});
1
2
3
4
5
6
7
8
9
10
11

方法 2:使用 hash.update()hash.digest() 计算哈希

需求场景:例如需要将 timestamptokenuserIduserName 的值拼凑成字符串,然后使用 SHA256 的方式加密生成一个「签名」字符串。

const crypto = require('crypto');
const str = `${timestamp},${token},${userId},${userName}`;

const sha256 = function (str) {
    const hash = crypto.createHash('sha256');
    hash.update(str);
    // 返回16进制hash码
    // 这里可以根据自己的需求
    // 另外要注意,返回的是大写字母
    return hash.digest('hex');
}

console.log(
    sha256(str);
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

结合方法 1 和 2

服务端流式读取文件内容并计算 MD5 值,这样可以减少内存消耗:

const fs = require('fs');
const crypto = require('crypto');
const fsSha1 = crypto.createHash('sha1');

const stream = fs.createReadStream(filepath);
const chunkArr = [];
stream.on('data', (chunk) => {
    fsSha1.update(chunk);
    chunkArr.push(chunk);
});
stream.on('end', () => {
    const buffer = Buffer.concat(chunkArr);
    // 整个文件内容的字节大小,可以作为 Content-length
    console.log(buffer.byteLength);

    const sha1 = fsSha1.digest('hex');
    // 文件内容的校验码
    console.log(sha1);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

当然,其实直接使用方法 1 的「管道流式」读取即可了。