來看各類加密算法在 Node.js 裏的實現

在大前端的趨勢下,前端er也要懂點數據加密相關的知識才行,加密算法的實現和原理咱們能夠不用深究,畢竟加密是一門高深的學科,可是基本的加密方式和編碼仍是要掌握的,畢竟沒吃過豬肉,豬跑仍是見過的嘛。javascript

我對常見的幾種加密和簽名的算法作個概括,同時附上 nodejs 的編碼實現。前端

加密算法

爲了保證數據的安全性和防篡改,不少數據在傳輸中都進行了加密。舉個場景的栗子,最近不少網站都升級到 https 協議, https 協議就是使用了非對稱加密和hash簽名,還有 github 使用的 ssh ,也是非對稱加密。還有大部分登陸時密碼採用的 MD5 加密等等。java

加密可分爲三大類,對稱加密和非對稱加密,還有摘要算法,咱們一一展開。node

對稱加密

引用百科的描述:git

採用單鑰密碼系統的加密方法,同一個密鑰能夠同時用做信息的加密和解密,這種加密方法稱爲對稱加密,也稱爲單密鑰加密。github

對稱加密很好理解,就比如我把我家的鑰匙給你,你要來我家,直接用這把鑰匙開門就行。算法

對稱加密

對稱加密目前主流的有 AESDESAES 是新一代的標準,速度快,安全級別更高。安全

AES

AES的加密模式有五種:CBC、ECB、CTR、OCF、CFB微信

  1. ECB:電子密本方式,須要一個密鑰便可,特色是簡單,利於並行計算。
  2. CBC:密文分組連接方式,除了須要一個密鑰以外,還須要一個向量,向量的做用也是用於數據的加密,因此這個的安全性要好於 ECB
  3. CTR、OCF、CFB:具體算法的實現方式不同,優缺點也各不相同,而這幾個都同 CBC 同樣,都須要密鑰和向量。

AES 有三種長度 128位、192位、256位,這三種的區別,主要來自於密鑰的長度,16字節密鑰=128位,24字節密鑰=192位,32字節密鑰=256位。以下表格:ssh

長度 密鑰長度 向量長度
128位 16 16
192位 24 16
256位 32 16

DES

加密默認與 AES 相同,也有五種模式,除了 ECB 只須要密鑰,其餘模式須要密鑰和向量。

AES 不一樣的是, DES 的密鑰長度只有8字節,向量也是8字節。

編碼實現

在 nodejs 中的實現

/** * @description * 對稱加密 * @param {*} data 加密數據 * @param {*} algorithm 加密算法 * @param {*} key 密鑰 * @param {*} iv 向量 * @returns */
function cipherivEncrypt(data, algorithm, key, iv) {
    const cipheriv = crypto.createCipheriv(algorithm, key, iv)
    let encrypted = cipheriv.update(data, 'utf8', 'hex');
    encrypted += cipheriv.final('hex');
    return encrypted
}

/** * @description * 對稱解密 * @param {*} data 解密數據 * @param {*} algorithm 解密算法 * @param {*} key 密鑰 * @param {*} iv 向量 * @returns */
function cipherivDecrypt(data, algorithm, key, iv) {
    const decipher = crypto.createDecipheriv(algorithm, key, iv);
    let decrypted = decipher.update(data, 'hex', 'utf8');
    decrypted += decipher.final('utf8');
    return decrypted
}
複製代碼

使用官方提供 crypto 庫來實現加解密,上面的代碼中加密後輸出的是 16 進制的字符串,你們能夠根據具體狀況換成其餘格式的數據。

調用方式以下

// AES對稱加解密
const str = 'xiaoliye';
const key = 'aaaaaaaaaaaaaaaaaaaaaaaa'; // 24
const iv = 'aaaaaaaaaaaaaaaaaaaaaaaa';  // 24
const cipherAesText = cipherivEncrypt(str, 'aes-192-cfb', key,iv)
const resultText = cipherivDecrypt(cipherAesText, 'aes-192-cfb', key,iv)
console.log(resultText === str) // true
複製代碼
// DES對稱加解密
const str = 'xiaoliye';
const key = 'aaaaaaaa'; //8
const iv = 'aaaaaaaa';  //8
const cipherAesText = cipherivEncrypt(str, 'des-cfb', key,iv)
const resultText = cipherivDecrypt(cipherAesText, 'des-cfb', key,iv)
console.log(resultText === str) / true
複製代碼

非對稱加密

非對稱加密,有兩把鑰匙,公鑰和私鑰,以下圖:

非對稱加密

公鑰是能夠公開對外,私鑰就是自個的,不可泄露。由於有兩個密鑰,非對稱加密這個名字就是這麼由來的。

發送方用接收方公開對外的公鑰進行加密,接收方收到數據後,用私鑰進行解密,業務處理完後,用私鑰給須要回傳的數據加密,收到數據的一方在用公鑰解密。

這個過程就是非對稱加解密,簡單理解就是公鑰加密的數據,用私鑰解密;私鑰加密的數據,用公鑰解密。

非對稱加密與對稱加密相比,安全性要高不少。對於對稱加密,密鑰鑰匙被某一方不當心泄露了,那祕文就有可能被破解和篡改。而非對稱加密,公鑰隨意流通,只要頒發密鑰的一方好好把私鑰保管好,安全性是妥妥的。

編碼實現

咱們來看 node 中的編碼實現,仍是使用官方提供的 crypto

  1. 加解密函數接收的數據是 Buffer 類型,(關於 Buffer 的介紹,不瞭解的朋友能夠看下這篇二進制與Buffer),因此須要約定好接收和輸出的數據類型。
  2. 參數 padding 是填充方式,有這麼幾種 crypto.constants.RSA_NO_PADDING, crypto.constants.RSA_PKCS1_PADDING, crypto.constants.RSA_PKCS1_OAEP_PADDING,由於沒有深刻研究過,就不展開啦,不過有一點,加密和解密的填充方式必須是要一致的。
const constants = require('constants')
const crypto = require('crypto')

/** * @description * 公鑰加密數據 * @param {*} data 待加密數據 * @param {*} publicKey 公鑰 * @param {*} inputEncoding 加密數據類型 * @param {*} outputEncoding 輸出的數據類型 * @param {*} padding 填充方式 * @returns */
function publicEncrypt(data, publicKey, inputEncoding, outputEncoding, padding) {
    const encryptText = crypto.publicEncrypt({
        key: publicKey,
        padding: padding || constants.RSA_PKCS1_PADDING
    }, Buffer.from(data, inputEncoding));

    return encryptText.toString(outputEncoding);
}

/** * @description * 公鑰解密數據 * @param {*} data 待解密數據 * @param {*} publicKey 公鑰 * @param {*} inputEncoding 解密數據類型 * @param {*} outputEncoding 輸出的數據類型 * @param {*} padding 填充方式 * @returns */
function publicDecrypt(data, publicKey, inputEncoding, outputEncoding, padding) {
    let decryptText = '';
    const decryptText = crypto.publicDecrypt({
        key: publicKey,
        padding: padding || constants.RSA_PKCS1_PADDING
    }, Buffer.from(data, inputEncoding));

    return decryptText.toString(outputEncoding);
}

/** * @description * 私鑰加密數據 * @param {*} data 待加密數據 * @param {*} privateKey 私鑰 * @param {*} inputEncoding 加密數據類型 * @param {*} outputEncoding 輸出的數據類型 * @param {*} padding 填充方式 * @returns */
function privateEncrypt(data, privateKey, inputEncoding, outputEncoding, padding) {
    const encryptText = crypto.privateEncrypt({
        key: privateKey,
        padding: padding || constants.RSA_PKCS1_PADDING
    }, Buffer.from(data, inputEncoding));

    return encryptText.toString(outputEncoding);
}

/** * @description * 私鑰解密數據 * @param {*} data 待解密數據 * @param {*} privateKey 私鑰 * @param {*} inputEncoding 解密數據類型 * @param {*} outputEncoding 輸出的數據類型 * @param {*} padding 填充方式 * @returns */
function privateDecrypt(data, privateKey, inputEncoding, outputEncoding, padding) {
    const decryptText = crypto.privateDecrypt({
        key: privateKey,
        padding: padding || constants.RSA_PKCS1_PADDING
    }, Buffer.from(data, inputEncoding));
    return decryptText.toString(outputEncoding);
}
複製代碼

有四個函數,分別是公鑰的加解密和私鑰的加解密,咱們看下如何使用,示例中是我本身生成的密鑰對,你們能夠自行替換

const rsaPublicKey = `-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCncWDMXEToSxtGQCmWY2ywl5CQ tb81PXYZch4v5M8MNUZPpcmf+VDXQbuWqqTqV/tY7rLviu/BAkFbX9NiFCapF5lP siVwSGWJQwq0S/++RCwB6yFVEzOKL25jANRBVNwmSOzojveCStYPcEs5Q829ld68 9TzluDDqUS69dTHGkQIDAQAB -----END PUBLIC KEY-----`

const rsaPrivateKey = `-----BEGIN PRIVATE KEY----- MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAKdxYMxcROhLG0ZA KZZjbLCXkJC1vzU9dhlyHi/kzww1Rk+lyZ/5UNdBu5aqpOpX+1jusu+K78ECQVtf 02IUJqkXmU+yJXBIZYlDCrRL/75ELAHrIVUTM4ovbmMA1EFU3CZI7OiO94JK1g9w SzlDzb2V3rz1POW4MOpRLr11McaRAgMBAAECgYEAhNa8/cQh4sxbKgOTOr1MKFlG Fpgpxroo7I0Nh9+Vp1DIpD2Z1PF9ghijEyf0R/pe7LIKgWIPTWdVpIFEeSYVeH43 FLr3zwR9oXzwG7RQTSN4d/Xcvg+24ZxCrvDfn7qDIlXh0jOS0wCvna1or7xgPcOu XG8J3BNbBdUixM0lk0ECQQDR4SCelWn0BY21jsFobX+pGqKOsj+tuvU4Cz47Gmev qvq2suYXwLemkP7EqRu8iNso/IzvrdsuJDG76dzwC4D5AkEAzDz2cDrKOVmqYw7s luOQFHl1TzmY7Umpd9YbZ5iXn0eCjIn1/e1risRF5+IeSpB84OVltUzj4cVDCbFd 9S1wWQJAIeKcFp5+9cPzxi1fMpIDO3Uua6WBvHXj44GFMZuow+byBY9KsOkPfZgJ Wg0Hil/6KlrkEkpaic+ULAetASCKWQJAdMh/Gdlj/LsaxJ2qBvWEU1DIFU8X9Mbk ElPpQ6lrOXaIXZgdgt8ZWTW1y0vuijBoV6iUKcEXpOdI1+gFk8YxsQJBAJsGJClf E1mE6CZgegM82428g4osZznVXBO/QtrQsA78S1xo8bo4qwVm0jQBcto65gwlfeeB Xm7MiIvNVBqzTVs= -----END PRIVATE KEY----- `

const str = 'xiaoliye'
const cipherText = publicEncrypt(str, rsaPublicKey, 'utf8', 'hex') // 公鑰加密
const decryptText = privateDecrypt(cipherText, rsaPrivateKey, 'hex', 'utf8') // 私鑰解密
console.log(str === decryptText) // true

const cipherTextPrivate = privateEncrypt(str,rsaPrivateKey,'utf8', 'hex') // 私鑰加密
const decryptTextPublic = publicDecrypt(cipherTextPrivate,rsaPublicKey, 'hex', 'utf8') // 公鑰解密
console.log(str === decryptTextPublic) // true

複製代碼

密鑰生成方式

網上有不少工具能夠一鍵生成配對的公鑰和私鑰,淘寶、微信都有提供相關工具,或者使用 OpenSSL 生成也能夠。

摘要算法(HASH)

把任意長度的輸入,根據算法生成一串固定長度的僞隨機數,這一算法就是摘要算法,它有這麼幾個特色

  1. 不須要密鑰,加密出來的數據沒法被解密,具備不可逆性。
  2. 生成的摘要長度是固定的,與輸入無關。
  3. 相同的輸入,使用相同的實現,生成的摘要必定相同;不一樣的輸入,生成的摘要是截然不同的,即,不會發生碰撞。

根據這些特色,摘要算法一般用於生成簽名,用來驗證數據的完整性。

還有用戶密碼的存儲,現在密碼的存儲主流的方式,就是使用摘要算法生成惟一的標識,爲了保證安全性,一般在生成摘要後再加上一串隨機數(加鹽salt),在來hash一次。

目前主流的實現有 MD5SHA-2MD5 生成的摘要是 32 字節, sha256 生成的摘要是 64 字節。

編碼實現

仍然是使用官方提供的 crypto

/** * @description * md5 * @param {*} data * @returns */
function md5(data){
    const hash = crypto.createHash('md5');
    return hash.update(data).digest('hex');
}

/** * @description * sha256 * @param {*} data * @returns */
function sha256(data){
    const hash = crypto.createHash('sha256');
    return hash.update(data).digest('hex');
}

console.log(md5('asdf')) // 912ec803b2ce49e4a541068d495ab570
console.log(sha256('asdf')) // f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13b
複製代碼

小結

涉及加密的活通常是後臺開發乾的,但前端靚仔懂點加密,會讓本身酷酷的~

小夥伴們還有遇到啥其餘加密的方式,歡迎一塊兒交流啊~

相關文章
相關標籤/搜索