注意:应该使用复杂的密码加密数据,简单的密码将被字典破解

第一种方法 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
};