博客地址:《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 在區塊鏈中有應用。這篇文章沒有再記錄,感興趣的同窗能夠去查閱相關資料。
👇掃碼關注「心譚博客」,查看「前端圖譜」&「算法題解」,堅持分享,共同成長👇