博客地址:《NodeJS模块研究 - crypto》Github :https://github.com/dongyuanxin/blogjavascript
nodejs 中的 crypto 模块提供了各类各样加密算法的 API。这篇文章记录了经常使用加密算法的种类、特色、用途和代码实现。其中涉及算法较多,应用面较广,每类算法都有本身适用的场景。为了使行文流畅,列出了本文记录的几类经常使用算法:html
散列函数(英语:Hash function)又称散列算法、哈希函数,是一种从任何一种数据中建立小的数字“指纹”的方法。基本原理是将任意长度数据输入,最后输出固定长度的结果。前端
hash 算法具备如下特色:java
正由于 hash 算法的这些特色,所以 hash 算法主要用于:加密、数据检验、版本标识、负载均衡、分布式(一致性 hash)。node
下面实现了一个获取文件标识的函数:git
const crypto = require("crypto"); const fs = require("fs"); function getFileHash(file, algorithm) { if (!crypto.getHashes().includes(algorithm)) { throw new Error("不支持此哈希函数"); } return new Promise(resolve => { const hash = crypto.createHash(algorithm); const rs = fs.createReadStream(file); rs.on("readable", () => { const data = rs.read(); if (data) { hash.update(data); } }); rs.on("end", () => { resolve(hash.digest("hex")); }); }); } // 用法:获取文件md5 getFileHash("./db.json", "md5").then(val => { console.log(val); });
攻击者能够借助“彩虹表”来破解哈希表。应对彩虹表的方法,是给密码加盐值(salt),将 pwd 和 salt 一块儿计算 hash 值。其中,salt 是随机生成的,越长越好,而且须要和用户名、密码对应保存在数据表中。github
虽然经过加盐,实现了哈希长度扩展,可是攻击者经过提交密码和哈希值也能够破解攻击。服务器会把提交的密码和 salt 构成字符串,而后和提交的哈希值对比。若是系统不能提交哈希值,不会受到此类攻击。算法
显然,没有绝对安全的方法。可是不推荐使用密码加盐,而是 HMac 算法。它可使用任意的 Hash 函数,例如 md5 => HmacMD五、sha1 => HmacSHA1。shell
下面是利用 Hmac 实现加密数据的函数:json
const crypto = require("crypto"); function encryptData(data, key, algorithm) { if (!crypto.getHashes().includes(algorithm)) { throw new Error("不支持此哈希函数"); } const hmac = crypto.createHmac(algorithm, key); hmac.update(data); return hmac.digest("hex"); } // output: 30267bcf2a476abaa9b9a87dd39a1f8d6906d1180451abdcb8145b384b9f76a5 console.log(encryptData("root", "7(23y*&745^%I", "sha256"));
有不少数据须要加密存储,而且须要解密后进行使用。这和前面不可逆的哈希函数不一样。此类算法一共分为两类:
查看 nodejs 支持的全部加密算法:
crypto.getCiphers();
Nodejs 提供了 Cipher 类和 Decipher 类,分别用于加密和解密。二者都继承 Transfrom Stream,API 的使用方法和哈希函数的 API 使用方法相似。
下面是用 aes-256-cbc 算法对明文进行加密:
const crypto = require("crypto"); const secret = crypto.randomBytes(32); // 密钥 const content = "hello world!"; // 要加密的明文 const cipher = crypto.createCipheriv( "aes-256-cbc", secret, Buffer.alloc(16, 0) ); cipher.update(content, "utf8"); // 加密后的结果:e2a927165757acc609a89c093d8e3af5 console.log(cipher.final("hex"));
注意:在使用加密算法的时候,给定的密钥长度是有要求的,不然会爆出this[kHandle].initiv(cipher, credential, iv, authTagLength); Error: Invalid key length...
的错误。以 aes-256-cbc 算法为例,须要 256 bits = 32 bytes 大小的密钥。一样地,AES 的 IV 也是有要求的,须要 128bits。(请参考“参考连接”部分)
使用 32 个连续I
做为密钥,用 aes-256-cbc 加密后的结果是 a061e67f5643d948418fdb150745f24d。下面是逆向解密的过程:
const secret = "I".repeat(32); const decipher = crypto.createDecipheriv( "aes-256-cbc", secret, Buffer.alloc(16, 0) ); decipher.update("a061e67f5643d948418fdb150745f24d", "hex"); console.log(decipher.final("utf8")); // 解密后的结果:hello world!
借助 openssl 生成私钥和公钥:
# 生成私钥 openssl genrsa -out privatekey.pem 1024 # 生成公钥 openssl rsa -in privatekey.pem -pubout -out publickey.pem
对 hello world!
加密和解密的代码以下:
const crypto = require("crypto"); const fs = require("fs"); const privateKey = fs.readFileSync("./privatekey.pem"); const publicKey = fs.readFileSync("./publickey.pem"); const content = "hello world!"; // 待加密的明文内容 // 公钥加密 const encodeData = crypto.publicEncrypt(publicKey, Buffer.from(content)); console.log(encodeData.toString("base64")); // 私钥解密 const decodeData = crypto.privateDecrypt(privateKey, encodeData); console.log(decodeData.toString("utf8"));
除了不可逆的哈希算法、数据加密算法,还有专门用于签名和验证的算法。这里也须要用 openssl 生成公钥和私钥。
代码示范以下:
const crypto = require("crypto"); const fs = require("fs"); const assert = require("assert"); const privateKey = fs.readFileSync("./privatekey.pem"); const publicKey = fs.readFileSync("./publickey.pem"); const data = "传输的数据"; // 第一步:用私钥对传输的数据,生成对应的签名 const sign = crypto.createSign("sha256"); // 添加数据 sign.update(data, "utf8"); sign.end(); // 根据私钥,生成签名 const signature = sign.sign(privateKey, "hex"); // 第二步:借助公钥验证签名的准确性 const verify = crypto.createVerify("sha256"); verify.update(data, "utf8"); verify.end(); assert.ok(verify.verify(publicKey, signature, "hex"));
从前面这段代码能够看到,利用私钥进行加密,获得签名值;最后利用公钥进行验证。
以前一直是只知其一;不知其二,一些概念很模糊,常常混淆散列算法和加密算法。整理完这篇笔记,我才理清楚了常见的加密算法的功能和用途。
除此以外,crypto 模块还提供了其余算法工具,例如 ECDH 在区块链中有应用。这篇文章没有再记录,感兴趣的同窗能够去查阅相关资料。
👇扫码关注「心谭博客」,查看「前端图谱」&「算法题解」,坚持分享,共同成长👇