Nodejs 中的 AES-256-GCM 解密

kd1*_*345 2 encryption node.js

我正在尝试解密存储在数据库中的数据,然后再将其发送到客户端。

我正在使用带有 AES-256-GCM 加密的内置加密模块。

我已成功实现加密并且工作正常,我的问题是我正在尝试解密不同文件中的数据,但我不断收到错误。

这是错误:

(node:35798) UnhandledPromiseRejectionWarning: TypeError [ERR_INVALID_ARG_TYPE]: The "iv" argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received undefined

1.js

router.post(
"/",
async(req, res) => {

function getFullAddress({housenumber, address1, address2, city, postcode, country}) {
return [housenumber, address1, ...(address2 ? [address2]: []), city, postcode, country].join(", ");
}

const aes256gcm = (key) => {

const encrypt = (str) => {
const iv = new crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);

let enc = cipher.update(str, 'utf8', 'base64');
enc += cipher.final('base64');
return Buffer.concat([Buffer.from(enc), iv, cipher.getAuthTag()]).toString("base64");
};

return {
encrypt,
};
};

const aesCipher = aes256gcm(key);

const hashedPasscode = await bcrypt.hash(req.body.passcode, 12);

        await User.create({
                            email: req.body.email,
                            mobilenumber: aesCipher.encrypt(req.body.mobilenumber),
                            passcode: hashedPasscode,
                            address: aesCipher.encrypt(getFullAddress(req.body))

                          })
Run Code Online (Sandbox Code Playgroud)

2.js

router.get(
"/",
async(req, res) => {

const aes256gcm = (key) => {

  const decrypt = (enc, iv, authTag) => {
    const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
    decipher.setAuthTag(authTag);
    let str = decipher.update(enc, 'base64', 'utf8');
    str += decipher.final('utf8');
    return str;
  };

  return {
    decrypt,
  };
};

const aesCipher = aes256gcm(key);

const decrypted_MobileNumber = aesCipher.decrypt(user.mobilenumber);
const decrypted_address = aesCipher.decrypt(user.address);

console.log('decrypted_MobileNumber',decrypted_MobileNumber)
console.log('decrypted_address',decrypted_address)
Run Code Online (Sandbox Code Playgroud)

这是我的数据库中存储的数据的示例

mobilenumber: 'Sm4xQjA2bmUwUUdEdW4zQkZ3PT3QEq5fBbTJ9ht4TgpQXTLmPYBSoQA836977j0rr3GYwg==',

Art*_* B. 7

这是您在加密期间所做的事情:

Buffer.concat([Buffer.from(enc), iv, cipher.getAuthTag()]).toString("base64");
Run Code Online (Sandbox Code Playgroud)

现在,您需要在解密过程中反转:

enc = Buffer.from(enc, "base64");
const iv = enc.slice(enc.length-32, enc.length-16);
const tag = enc.slice(enc.length-16);
enc = enc.slice(0, enc.length-32);
Run Code Online (Sandbox Code Playgroud)

第二个问题是 GCM 模式的 nonce/iv 应该是 12 字节长。我已经改变了这一点,因此上一期的一些索引也应该改变。

第三个问题是您无法连接加密和 Base64 编码的块。您必须在 Base64 编码之前将它们连接起来,以便字符串中间没有 Base64 填充。对于 GCM 来说这不应该是一个大问题,因为调用cipher.final('base64');应该返回一个空字符串。

第四个也是明显的问题是,在加密过程中,您编码了两次,但您只需要编码一次。

一起看起来像这样:

const crypto = require('crypto');

const aes256gcm = (key) => {

  const encrypt = (str) => {
    const iv = new crypto.randomBytes(12);
    const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);

    let enc1 = cipher.update(str, 'utf8');
    let enc2 = cipher.final();
    return Buffer.concat([enc1, enc2, iv, cipher.getAuthTag()]).toString("base64");
  };

  const decrypt = (enc) => {
    enc = Buffer.from(enc, "base64");
    const iv = enc.slice(enc.length - 28, enc.length - 16);
    const tag = enc.slice(enc.length - 16);
    enc = enc.slice(0, enc.length - 28);
    const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
    decipher.setAuthTag(tag);
    let str = decipher.update(enc, null, 'utf8');
    str += decipher.final('utf8');
    return str;
  };

  return {
    encrypt,
    decrypt,
  };
};

const cipher = aes256gcm(Buffer.alloc(32)); // just a test key
const ct = cipher.encrypt('test');
const pt = cipher.decrypt(ct);
console.log(pt);
Run Code Online (Sandbox Code Playgroud)