crypto 加密子產品
-
子產品提供了加密功能,包括對OpenSSL 的哈希、HMAC、加密、解密、簽名、以及驗證功能的以整套封裝。crypto
- 使用
來通路該子產品require('crypto')
- 檢視nodejs支援的加密算法,
- 使用
,如下所示crypto.getCiphers()
const crypto = require('crypto') crypto.getCiphers()
- 得到的是一個比較大的數組,這裡列舉其中的幾個元素:
[
‘aes-128-cbc’, ‘aes-128-ccm’, ‘aes-128-cfb’,
‘aes256’, ‘aes256-wrap’, ‘aria-128-cbc’,
‘aria-128-ccm’, ‘aria-128-cfb’, ‘aria-128-cfb1’,
‘bf-cbc’, ‘bf-cfb’, ‘bf-ecb’,
‘camellia192’, ‘camellia256’, ‘cast’,
‘cast-cbc’,
… 71 more items
]
- 這裡重點介紹 ASE 加密
- ASE加密是進階加密标準,為美國聯邦政府采用的一種區塊加密标準,進階加密标準已然成為對稱密鑰加密中最流行的算法之一。
- ASE使用的密鑰長度可以128位、192位或256位,是以你可以看到加密算法:aes-128/196/256,表示的都是密鑰的位數。最後一段是AES的工作模式,最常用的是 ECB、CBC、CFB、和OFB四種。
- ASE 加密時的key(原始密鑰) 和 iv(初始化向量的長度)
長度 密鑰長度 向量長度 128位 16 16 192位 24 16 256位 32 16
- 使用
-
方法:crypto.scrypt(password,salt,keylen[,options],callback)
-
是一個異步的密鑰派生函數,被設計為在計算和記憶體方面的成本都非常高,目的是使它無法暴力破解。scrypt()
-
: 鹽值,應該盡可能獨特。建議鹽值是随機的并且至少16個位元組長。salt
-
: 生成的密鑰的長度。keylen
-
回調函數有兩個參數:callback
和err
, 當密鑰派生失敗時, err 是一個異常對象,否則 err 為 null。 derivedKey 會作為 Buffer 傳給回調。derivedKey
-
-
方法:crypto.randomBytes(size[,callback])
- 生成加密的強僞随機資料。size 參數是生成随機數的位元組數。
- callback 回調函數有兩個參數:
和err
。如果發生錯誤,則buf
是一個err
對象,否則為Error
。null
參數是包含生成位元組的buf
。Buffer
###
Cipher
類
-
類Cipher
- Cipher 是密碼的意思。Cipher` 類執行個體用來加密資料。使用方式有以下兩種,任選一種即可:
- 作為一個可讀可寫的stream流,這樣可以将原生未加密的資料寫入并在可讀側生成加密的資料。
- 使用 cipher.update 和 cipher.final 方法來生成加密資料
-
方法:crypto.createCipheriv(algorithm, key, iv[, options])
- 使用給定的
(算法)、algorithm
和初始化向量key
建立并傳回一個iv
對象。Cipher
-
取決于 OpenSSL,列如:algorithm
'aes-128-cbc'
-
是key
使用的原始密鑰,algorithm
是初始化向量。兩個參數都必須是iv
編碼的字元串、utf8
、Buffer
或TypedArray
。DataView
- 初始化向量應該是不可預測的且唯一的,理想情況下,它們在密碼上是随機的。
- 使用給定的
-
方法:cipher.update(data[, inputEncoding][, outputEncoding])
- 加密
,生産加密資料。 如果指定了data
參數,則inputEncoding
參數是使用了指定的字元編碼的字元串。 如果未指定data
參數,則inputEncoding
必須是一個data
、Buffer
或TypedArray
。 如果DataView
是一個data
、Buffer
或TypedArray
,則DataView
會被忽略。inputEncoding
-
指定了加密的資料的輸出格式。 如果指定了outputEncoding
,則傳回使用了指定的字元編碼的字元串。 如果未提供outputEncoding
,則傳回outputEncoding
。Buffer
- 可以使用新資料多次調用
方法,直到cipher.update()
被調用。cipher.final()
- 加密
-
方法:cipher.final([outputEncoding])
- 傳回任何剩餘的加密内容。如果指定了
,則傳回一個字元串。如果未提供outputEncoding
,則傳回outputEncoding
。Buffer
- 一旦調用了
方法,則cipher.final()
對象就不能再用于加密資料。 如果試圖多次調用Cipher
,則将會導緻抛出錯誤。cipher.final()
- 傳回任何剩餘的加密内容。如果指定了
- 注意:加密時要将生成密鑰的密碼:
,鹽值:password
以及初始化向量salt
一同寫入密文,解密時通過密文來擷取iv
、password
、salt
iv
###
Decipher
類
-
類的執行個體用于解密資料。 該類可以通過以下兩種方式之一使用:Decipher
- 作為可讀寫的流,其中寫入加密的資料以在可讀側生成未加密的資料。
- 使用
和decipher.update()
方法生成未加密的資料。decipher.final()
-
方法:crypto.createDecipheriv(algorithm, key, iv[, options])
- 使用給定的 algorithm(算法)、 key 和初始化向量(iv)建立并傳回一個 Decipher 對象。
-
:算法algorithm
-
: 是key
使用的原始密鑰algorithm
-
: 初始化向量iv
-
方法:decipher.update(data[, inputEncoding][, outputEncoding])
- 解密使用
類執行個體加密後的密文Cipher
-
: 如果指定了 inputEncoding 參數,則 data 參數是使用了指定的字元編碼的字元串。inputEncoding
-
指定了解密的資料的輸出格式outputEncoding
- 解密使用
-
方法:decipher.final([outputEncoding])
- 傳回任何剩餘的解密内容
- 如果指定了
,則傳回一個字元串。如果未提供outputEncoding
,則傳回outputEncoding
。Buffer
- 注意:解密時的算法,密鑰密碼, 鹽值,初始化向量必須要和加密時的一緻,否則就會出現下面常見的錯誤:
06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
加密解密子產品
加密解密子產品實作:
- 加密解密子產品被放在了一個叫 tools 的工具檔案夾下面
- cipherCode.js
//============ 加密/解密資料子產品 ===========
'use strict';
const crypto = require('crypto');
//------------------ 加密資料 -------------------
let algorithm = 'aes-128-cbc'; // algorithm 是算法的意思
/**
* @description 加密資料
* @description params: data--要加密的資料,必須是utf8格式的字元串;callback--處理結果集的回調函數
* @param data {string}
* @param callback {function(string)}
*/
let encrypt = function (data, callback) {
let password = crypto.randomBytes(16).toString('hex'); // password是用于生産密鑰的密碼
let salt = crypto.randomBytes(16).toString('hex'); // 生成鹽值
let iv = crypto.randomBytes(8).toString('hex'); // 初始化向量
crypto.scrypt(password, salt, 16, function (err, derivedKey) {
if (err) {
throw err;
} else {
let cipher = crypto.createCipheriv(algorithm, derivedKey, iv); // 建立 cipher 執行個體
// 加密資料
let cipherText = cipher.update(data, 'utf8', 'hex');
cipherText += cipher.final('hex');
cipherText += (password+salt + iv);
callback(cipherText)
}
});
};
/**
* @description 解密通過 encrypt(); 加密的資料
* @description param: cipherText--通過 encrypt() 加密的資料; callback--處理結果集的回調函數。
* @param cipherText {string}
* @param callback {function(string)}
*/
let decrypt = function (cipherText, callback) {
let iv = cipherText.slice(-16); // 擷取初始化向量
let salt = cipherText.slice(-48, -16); // 擷取鹽值
let password = cipherText.slice(-80, -48); // 擷取密鑰密碼
let data = cipherText.slice(0, -80); //擷取密文
crypto.scrypt(password, salt, 16, function (err, derivedKey) {
if (err) {
throw err;
} else {
let decipher = crypto.createDecipheriv(algorithm, derivedKey, iv); // 建立 decipher 執行個體
// 解密資料
let txt = decipher.update(data, 'hex', 'utf8');
txt += decipher.final('utf8');
callback(txt)
}
});
};
//----------- 導出 加密/解密資料子產品 --------------
module.exports = {
encrypt,
decrypt
};
- index.js
//=========== 工具子產品 ==========
const cipher = require('./cipherCode.js');
module.exports = {
cipher
};
測試
// =========== 測試加/解密子產品 =============
const fs = require('fs');
const path = require('path');
const tools = require('../tools');
let str = '今年(應是2003年)是我大學畢業滿10年的日子,也是我投身IT技術的第10年。';
tools.cipher.encrypt(str, function (result) {
fs.writeFile(path.join(__dirname, '1.txt'), result, function (err) {
console.log('加密資料寫入成功');
});
});
fs.readFile(path.join(__dirname, '1.txt'), 'utf8', function (err, info) {
tools.cipher.decrypt(info, function (result) {
console.log(result);
console.log(result===str);
});
});
- 測試結果
加密資料寫入成功
今年(應是2003年)是我大學畢業滿10年的日子,也是我投身IT技術的第10年。
true