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
};