nodejs AES加密
注意:应该使用复杂的密码加密数据,简单的密码将被字典破解
第一种方法 CTR模式
const crypto = require('crypto'); const algorithm = 'aes-256-ctr'; const secretKey = 'put complex password in there'; const iv = crypto.randomBytes(16); const encrypt = (text) => { const cipher = crypto.createCipheriv(algorithm, secretKey, iv); const encrypted = Buffer.concat([cipher.update(text), cipher.final()]); return { iv: iv.toString('hex'), content: encrypted.toString('hex') }; }; const decrypt = (hash) => { const decipher = crypto.createDecipheriv(algorithm, secretKey, Buffer.from(hash.iv, 'hex')); const decrpyted = Buffer.concat([decipher.update(Buffer.from(hash.content, 'hex')), decipher.final()]); return decrpyted.toString(); }; module.exports = { encrypt, decrypt };
第二种参考mask的方法 GCM模式
https://github.com/MetaMask/browser-passworder
Details
The serialized text is stored as a JSON blob that includes three base64-encoded fields, data, iv, and salt, none of which you need to worry about.
A key is derived from the password using PBKDF2 with a salt sampled from crypto.getRandomValues(). The data is encrypted using the AES-GCM algorithm with an initialization vector sampled from crypto.getRandomValues().
GCM模式可以可以将这一部分作为附加消息加入到MAC值的计算当中,而CTR没有附加消息。具体区别详见文章 /4797.html
const crypto = require('crypto').webcrypto; const DERIVED_KEY_FORMAT = 'AES-GCM'; const STRING_ENCODING = 'utf-8'; // encrypt async function encrypt(password, dataObj, key, salt = generateSalt()) { const cryptoKey = key || (await keyFromPassword(password, salt)); const payload = await encryptWithKey(cryptoKey, dataObj); payload.salt = salt; return JSON.stringify(payload); } function generateSalt(byteCount = 32) { const view = new Uint8Array(byteCount); crypto.getRandomValues(view); const b64encoded = btoa(String.fromCharCode.apply(null, view)); return b64encoded; } async function encryptWithKey(key, dataObj) { const data = JSON.stringify(dataObj); const dataBuffer = Buffer.from(data, STRING_ENCODING); const vector = crypto.getRandomValues(new Uint8Array(16)); const buf = await crypto.subtle.encrypt({ name: DERIVED_KEY_FORMAT, iv: vector, }, key, dataBuffer); const buffer = new Uint8Array(buf); const vectorStr = Buffer.from(vector).toString('base64'); const vaultStr = Buffer.from(buffer).toString('base64'); return { data: vaultStr, iv: vectorStr, }; } // decrypt async function decrypt(password, text, key) { const payload = JSON.parse(text); const { salt } = payload; const cryptoKey = key || (await keyFromPassword(password, salt)); const result = await decryptWithKey(cryptoKey, payload); return result; } async function keyFromPassword(password, salt, exportable = false) { const passBuffer = Buffer.from(password, STRING_ENCODING); const saltBuffer = Buffer.from(salt, 'base64'); const key = await crypto.subtle.importKey('raw', passBuffer, { name: 'PBKDF2' }, false, ['deriveBits', 'deriveKey']); const derivedKey = await crypto.subtle.deriveKey({ name: 'PBKDF2', salt: saltBuffer, iterations: 10000, hash: 'SHA-256', }, key, { name: DERIVED_KEY_FORMAT, length: 256 }, exportable, ['encrypt', 'decrypt']); return derivedKey; } async function decryptWithKey(key, payload) { const encryptedData = Buffer.from(payload.data, 'base64'); const vector = Buffer.from(payload.iv, 'base64'); let decryptedObj; try { const result = await crypto.subtle.decrypt({ name: DERIVED_KEY_FORMAT, iv: vector }, key, encryptedData); const decryptedData = new Uint8Array(result); const decryptedStr = Buffer.from(decryptedData).toString(STRING_ENCODING); decryptedObj = JSON.parse(decryptedStr); } catch (e) { throw new Error('Incorrect password'); } return decryptedObj; } function decodeMnemonic(mnemonic) { if (typeof mnemonic === 'string') { return mnemonic; } else { return Buffer.from(mnemonic).toString('utf8'); } } module.exports = { encrypt, decrypt, decodeMnemonic };