加密是以某種算法改變原有的信息數據,使得未受權用戶即便得到了已加密信息,因不知解密的方法,沒法得知信息真正的含義,經過這種方式提升網絡數據傳輸的安全性,加密算法常見的有哈希算法、HMAC 算法、簽名、對稱性加密算法和非對稱性加密算法,加密算法也分爲可逆和不可逆,好比 md5
就是不可逆加密,只能暴力破解(撞庫),咱們在 NodeJS 開發中就是直接使用這些加密算法,crypto
模塊提供了加密功能,包含對 OpenSSL
的哈希、HMAC、加密、解密、簽名以及驗證功能的一整套封裝,核心模塊,使用時不需安裝。算法
哈希算法也叫散列算法,用來把任意長度的輸入變換成固定長度的輸出,常見的有 md5
、sha1
等,這類算法實現對原數據的轉化過程是否能被稱爲加密備受爭議,爲了後面敘述方便咱們姑且先叫作加密。瀏覽器
const crypto = require("crypto");
// getHashes 方法用於查看支持的加密算法
console.log(crypto.getHashes());
// [ 'DSA', 'DSA-SHA', 'DSA-SHA1', 'DSA-cSHA1-old',
// 'RSA-MD4', 'RSA-MD5', 'RSA-MDC2', 'RSA-RIPEMD160',
// 'RSA-SHA', 'RSA-SHA1', 'RSA-SHA1-2', 'RSA-SHA224',
// 'RSA-SHA256', 'RSA-SHA384', 'RSA-SHA512',
// 'dsaEncryption', 'dsaWithSHA', 'dsaWithSHA1', 'dss1',
// 'ecdsa-with-SHA1', 'md4', 'md4WithRSAEncryption',
// 'md5', 'md5WithRSAEncryption', 'mdc2', 'mdc2WithRSA',
// 'ripemd', 'ripemd160', 'ripemd160WithRSA', 'rmd160',
// 'sha', 'sha1', 'sha1WithRSAEncryption', 'sha224',
// 'sha224WithRSAEncryption', 'sha256',
// 'sha256WithRSAEncryption', 'sha384',
// 'sha384WithRSAEncryption', 'sha512',
// 'sha512WithRSAEncryption', 'shaWithRSAEncryption',
// 'ssl2-md5', 'ssl3-md5', 'ssl3-sha1', 'whirlpool' ]複製代碼
md5
是開發中常用的算法之一,官方稱爲摘要算法,具備如下幾個特色:緩存
因爲相同的輸入通過 md5
加密後返回的結果徹底相同,因此破解時經過 「撞庫」 進行暴力破解,當連續被 md5
加密 3
次以上時就很難被破解了,因此使用 md5
通常會進行屢次加密。安全
const crytpo = require("crytpo");
let md5 = crytpo.createHash("md5"); // 建立 md5
let md5Sum = md5.update("hello"); // update 加密
let result = md5Sum.digest(); // 獲取加密後結果
console.log(result); // <Buffer 5d 41 40 2a bc 4b 2a 76 b9 71 9d 91 10 17 c5 92>複製代碼
digest
方法參數用於指定加密後的返回值的格式,不傳參默認返回加密後的 Buffer,經常使用的參數有 hex
和 Base64
,hex
表明十六進制,加密後長度爲 32
,Base64
的結果長度爲 24
,以 ==
結尾。bash
const crypto = require("crypto");
let md5 = crypto.createHash("md5");
let md5Sum = md5.update("hello");
let result = md5Sum.digest("hex");
console.log(result); // 5d41402abc4b2a76b9719d911017c592複製代碼
const crypto = require("crypto");
let md5 = crypto.createHash("md5");
let md5Sum = md5.update("hello");
let result = md5Sum.digest("Base64");
console.log(result); // XUFAKrxLKna5cZ2REBfFkg==複製代碼
update
方法的返回值就是 this
,即當前實例,因此支持鏈式調用,較長的信息也能夠屢次調用 update
方法進行分段加密,調用 digest
方法一樣會返回整個加密後的值。服務器
const crypto = require("crypto");
let result = crypto
.createHash("md5")
.update("he")
.update("llo")
.digest("hex");
console.log(result); // 5d41402abc4b2a76b9719d911017c592複製代碼
因爲可使用 update
進行分段加密,就能夠結合流來使用,其實 crypto
的本質是建立 Transform
類型的轉化流,能夠將可讀流轉化成可寫流。cookie
const crypto = require("crypto");
let fs = require("fs");
let md5 = crypto.createHash("md5");
let rs = fs.createReadSteam("./readme.txt", {
highWaterMark: 3
});
// 讀取數據並加密
rs.on("data", data => md5.update(data));
rs.on("end", () => {
let result = md5.digest("hex");
console.log(result);
});複製代碼
md5
加密摘要後的暗文,接收端拿到數據之後將明文摘要按照相同的 md5
算法加密後與暗文摘要對比驗證,目的是防止數據傳輸過程當中被劫持並篡改。
md5
加密,每次向服務器發送加密後的密鑰進行比對就能夠了,不至於對整個文件內容進行比較。
md5
的哈希算法加密,別人可使用一樣的算法對信息進行僞造,安全性不高。
Hmac 算法又稱加鹽算法,是將哈希算法與一個密鑰結合在一塊兒,用來阻止對簽名完整性的破壞,一樣具有 md5
加密的幾個特色。網絡
const crytpo = require("crytpo");
let hmac = crytpo.createHmac("sha1", "panda");
let result = hmac.update("hello").digest("Base64");
console.log(result); // 7spMLxN8WJdcEtQ8Hm/LR9pUE3YsIGag9Dcai7lwioo=複製代碼
crytpo.createHmac
第一個參數同 crytpo.createHash
,爲加密的算法,經常使用 sha1
和 sha256
,第二個參數爲密鑰。ui
digest
方法生成的加密結果長度要大於 md5
,hex
生成的結果長度爲 64
,Base64
生成的結果長度爲 44
,以 =
結尾。this
md5
,經過密鑰來加密,不知道密鑰沒法破解,缺點是密鑰傳輸的過程容易被劫持,能夠經過一些生成隨機密鑰的方式避免。
能夠安裝 openSSH
客戶端,並經過命令行生成存儲密鑰的文件,命令以下。
openssl genrsa -out rsa_private.key 1024
openssl genrsa
表明生成密鑰,-out
表明輸出文件,rsa_private.key
表明文件名,1024
表明輸出密鑰的大小。
const fs = require("fs");
const crytpo = require("crytpo");
const path = require("path");
let key = fs.readFileSync(path.join(__dirname, "/rsa_private.key"));
let hmac = crytpo.createHmac("sha256", key);
let result = hmac.update("hello").digest("Base64");
console.log(result); // bmi2N+6kwgwt5b+U+zSgjL/NFs+GsUnZmcieqLKBy4M=複製代碼
對稱性加密是發送數據時使用密鑰和加密算法進行加密,接收數據時須要使用相同的密鑰和加密算法的逆算法(解密算法)進行解密,也就是說對稱性加密的過程是可逆的,crytpo
中使用的算法爲 blowfish
。
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("hello");
// final 方法不能鏈式調用
let result = cipher.final("hex");
console.log(result); // 3eb9943113c7aa1e
// 解密
let decipher = crypto.createDecipher("blowfish", key);
decipher.update(result, "hex");
let data = decipher.final("utf8");
console.log(data); // hello複製代碼
加密使用 crypto.createCipher
方法,解密使用 crypto.createDecipher
方法,可是使用的算法和密鑰必須相同,須要注意的是解密過程當中 update
中須要在第二個參數中指定加密時的格式,如 hex
,在 final
還原數據時須要指定加密字符的編碼格式,如 utf8
。
7
個字符,不然雖然能夠加密成功,可是沒法解密。
非對稱性加密相也是可逆的,較於對稱性加密要更安全,消息傳輸方和接收方都會在本地建立一對密鑰,公鑰和私鑰,互相將本身的公鑰發送給對方,每次消息傳遞時使用對方的公鑰加密,對方接收消息後使用他的的私鑰解密,這樣在公鑰傳遞的過程當中被截獲也沒法解密,由於公鑰加密的消息只有配對的私鑰能夠解密。
接下來咱們使用 openSSH
對以前生成的私鑰 rsa_private.key
產生一個對應的公鑰,命令以下。
openssl rsa -in rsa_private.key -pubout -out rsa_public.key
上面的命令意思根據一個私鑰生成對應的公鑰,-pubout -out
表明公鑰輸出,rsa_public.key
爲公鑰的文件名。
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 = crytpo.publicEncrypt(publicKey, Buffer.from("hello"));
// 解密
let result = crytpo.provateDecrypt(privateKey, secret);
console.log(result); // hello複製代碼
使用公鑰加密的方法是 crytpo.publicEncrypt
,第一個參數爲公鑰,第二個參數爲加密信息(必須是 Buffer),使用私鑰解密的方法是 crytpo.provateDecrypt
,第一個參數爲私鑰,第二個參數爲解密的信息。
簽名與非對稱性加密很是相似,一樣有公鑰和私鑰,不一樣的是使用私鑰加密,對方使用公鑰進行解密驗證,以確保這段數據是私鑰的擁有者所發出的原始數據,且在網絡中的傳輸過程當中未被修改。
咱們還使用 rsa_public.key
和 rsa_private.key
做爲公鑰和私鑰,crypto
實現簽名代碼以下。
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("panda");
let signed = sign.sign(privateKey, "hex");
// 驗證簽名
let verify = crypto.createVerify("RSA-SHA256");
verify.update("panda");
let verifyResult = verify.verify(publicKey, signed, "hex");
console.log(verifyResult); // true複製代碼
生成簽名的 sign
方法有兩個參數,第一個參數爲私鑰,第二個參數爲生成簽名的格式,最後返回的 signed
爲生成的簽名(字符串)。
驗證簽名的 verify
方法有三個參數,第一個參數爲公鑰,第二個參數爲被驗證的簽名,第三個參數爲生成簽名時的格式,返回爲布爾值,便是否經過驗證。
各類項目在數據傳輸時根據信息的敏感度以及用途進行不一樣的加密算法和加密方式,在 NodeJS 中,crypto
的 API 徹底能夠實現咱們的加密需求,也能夠將上面的加密方案組合使用實現更復雜的加密方案。