nodejs - Crypto模块

简介加密

加密是以某种算法改变原有的信息数据,使得未授权用户即使获得了已加密信息,因不知解密的方法,无法得知信息真正的含义通过这种方式提高网络数据传输的安全性.

加密算法常见的有一下五类:

  1. 哈希算法
  2. HMAC 算法
  3. 签名
  4. 对称性加密算法
  5. 非对称性加密算法

加密算法也分为可逆不可逆,比如 md5 就是不可逆加密,只能暴力破解(撞库),node利用 OpenSSL库来实现它的加密技术,crypto 模块提供了加密功能,包含对 OpenSSL 的哈希、HMAC、加密、解密、签名以及验证功能的一整套封装,核心模块,使用时不需安装。

第一类 - hash算法/哈希算法/散列算法

  • 用来把任意长度的输入变换成固定长度的输出
  • 常见的有 md5、sha1,还有例如sha256,sha512,ripemd160-crypto.getHashes()方法用于查看支持的加密算法

md5 - 也叫’摘要算法’

  • 具有以下几个特点
  1. 不可逆;
  2. 不管加密的内容多长,最后输出的结果长度都是相等的;
  3. 内容不同输出的结果完全不同,内容相同输出的结果完全相同
  • 暴力破解

    由于相同的输入经过 md5 加密后返回的结果完全相同,所以破解时通过 “撞库” 进行暴力破解,当连续被 md5 加密 3 次以上时就很难被破解了,所以使用 md5 一般会进行多次加密

  • 使用

    1
    2
    3
    4
    let md5 = crytpo.createHash("md5"); // 创建 md5
    let md5Sum = md5.update("123"); // update 加密
    let result = md5Sum.digest(); // 获取加密后结果
    // result <Buffer 20 2c b9 62 ac 59 07 5b 96 4b 07 15 2d 23 4b 70>

update方法的返回值就是 this,即当前实例,所以支持链式调用,较长的信息也可以多次调用 update 方法进行分段加密,调用 digest 方法同样会返回整个加密后的值

1
2
3
4
5
crypto
.createHash("md5")
.update("admin")
.update("123")
.digest("hex"); // 0192023a7bbd73250516f069df18b500

digest 方法参数用于指定加密后的返回值的格式,不传参默认返回加密后的 Buffer,常用的参数有 hex 和 Base64,hex 代表十六进制,加密后长度为 32,Base64 的结果长度为 24,以 == 结尾

1
2
crypto.createHash('md5').update('123').digest('hex') // 202cb962ac59075b964b07152d234b70
crypto.createHash('md5').update('123').digest('base64')//'ICy5YqxZB1uWSwcVLSNLcA=='

第二类 Hmac 算法 - 也叫加盐算法

  • 是将哈希算法一个密钥结合在一起,用来阻止对签名完整性的破坏,同样具备 md5 加密的几个特点

  • 使用

    1
    crypto.createHmac("sha1", "salt").update('admin123').digest('hex'); // 629a619b75bd12ecb66a749558ba5b4f9703d531

createHmac(‘加密的算法’,’密钥’) - 第一个参数同 crytpo.createHash,为加密的算法,常用 sha1 和 sha256,第二个参数为密钥

digest 方法生成的加密结果长度要大于 md5(因为Hmac算法常用 sha1 和 sha256),hex 生成的结果长度为 64,Base64 生成的结果长度为 44,以 = 结尾

创建密钥的方法

  • 可以安装 openSSH 客户端,并通过命令行生成存储密钥的文件,命令如下。

openssl genrsa -out rsa_private.key 1024

openssl genrsa 代表生成密钥,-out 代表输出文件,rsa_private.key 代表文件名,1024 代表输出密钥的大小

1
2
3
4
5
6
7
8
// 直接读取密钥文件配合加盐算法加密
const fs = require("fs");
const crypto = require("crypto");
const path = require("path");

let key = fs.readFileSync(path.join(__dirname, "/rsa_private.key"));
let hmac = crypto.createHmac("sha256", key);
let result = hmac.update("admin123").digest("Base64");

第三类 对称性加密

  • 对称性加密是发送数据时使用密钥和加密算法进行加密,接收数据时需要使用相同的密钥和加密算法的逆算法(解密算法)进行解密,也就是说对称性加密的过程是可逆的,crypto 中使用的算法为 blowfish

  • 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 对称性加密
const fs = require("fs");
const crypto = require("crypto");
const path = require("path");

let key = fs.readFileSync(path.join(__dirname, "/rsa_private.key"));

// 加密
let cipher = crypto.createCipher("blowfish", key);
cipher.update("admin123");

// final 方法不能链式调用
let result = cipher.final("hex");
console.log(result); // 7c827dbc0b9ccc4d

// 解密
let decipher = crypto.createDecipher("blowfish", key);
decipher.update(result, "hex");

let data = decipher.final("utf8");
console.log(data); // admin123
  • 加密使用 crypto.createCipher 方法,解密使用 crypto.createDecipher 方法,但是使用的算法和密钥必须相同,需要注意的是解密过程中 update 中需要在第二个参数中指定加密时的格式,如 hex,在 final 还原数据时需要指定加密字符的编码格式,如 utf8。

第四类 非对称性加密

  • 非对称性加密相也是可逆的,较于对称性加密要更安全,消息传输方和接收方都会在本地创建一对密钥,公钥和私钥,互相将自己的公钥发送给对方,每次消息传递时使用对方的公钥加密,对方接收消息后使用他的私钥解密,这样在公钥传递的过程中被截获也无法解密,因为公钥加密的消息只有配对的私钥可以解密。

  • 使用 openSSH 对之前生成的私钥 rsa_private.key 产生一个对应的公钥

openssl rsa -in rsa_private.key -pubout -out rsa_public.key

上面的命令意思根据一个私钥生成对应的公钥,-pubout -out 代表公钥输出,rsa_public.key 为公钥的文件名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 非对称性加密
const fs = require("fs");
const crypto = require("crypto");
const path = require("path");

// 获取公钥和私钥
let publicKey = fs.readFileSync(path.join(__dirname, "/rsa_public.key"));
let privateKey = fs.readFileSync(path.join(__dirname, "/rsa_private.key"));

// 加密
let secret = crypto.publicEncrypt(publicKey, Buffer.from("hello"));

// 解密
let result = crypto.privateDecrypt(privateKey, secret);

console.log(result); // hello

使用公钥加密的方法是 crypto.publicEncrypt,第一个参数为公钥,第二个参数为加密信息(必须是 Buffer),使用私钥解密的方法是 crypto.privateDecrypt,第一个参数为私钥,第二个参数为解密的信息。

签名

签名与非对称性加密非常类似,同样有公钥和私钥,不同的是使用私钥加密,对方使用公钥进行解密验证,以确保这段数据是私钥的拥有者所发出的原始数据,且在网络中的传输过程中未被修改。

  • 使用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // 签名
    const fs = require("fs");
    const crypto = require("crypto");
    const path = require("path");

    // 获取公钥和私钥
    let publicKey = fs.readFileSync(path.join(__dirname, "rsa_public.key"), "ascii");
    let privateKey = fs.readFileSync(path.join(__dirname, "rsa_private.key"), "ascii");

    // 生成签名
    let sign = crypto.createSign("RSA-SHA256");
    sign.update("admin123");
    let signed = sign.sign(privateKey, "hex");

    // 验证签名
    let verify = crypto.createVerify("RSA-SHA256");
    verify.update("admin123");
    let verifyResult = verify.verify(publicKey, signed, "hex");

    console.log(verifyResult); // true

生成签名的 sign 方法有两个参数,第一个参数为私钥,第二个参数为生成签名的格式,最后返回的 signed 为生成的签名(字符串)。

验证签名的 verify 方法有三个参数,第一个参数为公钥,第二个参数为被验证的签名,第三个参数为生成签名时的格式,返回为布尔值,即是否通过验证。

总结

createHash - 哈希算法,常用md5的方式,还有sha1,sha256等,不可逆,加密源数据一样生成的结果一样,长度固定。

createHmac - 用随机数“增强”的哈希算法,加salt,常用sha1,sha256等方式加密。

对称性加密 - 使用相同的密钥进行加密和解密处理

非对称性加密 - 双方都生成一对公钥和私钥,APublic,APrivate,BPublic,BPrivate,将自己的公钥都给对方,然后传输前使用对方的公钥加密,使用自己的私钥进行解密

签名 - 使用私钥进行加密,然后传输,最后通过公钥进行解密

参考文献

NodeJS 加密 —— crypto 模块
密码安全性策略
NodeJS开发教程14-Crypto加密与解密

初到贵宝地,有钱的给个钱场,没钱的挤一挤给个钱场