来自节点 crypto.createCipheriv('aes-256-gcm', ...).getAuthKey() 的值可以是 public 吗?

Can the value from node crypto.createCipheriv('aes-256-gcm', ...).getAuthKey() be public?

我找不到一些信息。有谁知道从 cipher.getAuthTag() (--> returns MAC) 返回的值是否可以公开可见?

TL;DR

Can a message authentication code be publicly visible, or does this need to be kept secret like a password?

一些背景,我正在尝试加密文件。我发现这个 Whosebug 问题和答案帮助我开始。

在对 nodejs 文档进行一些研究后,我发现答案使用了一个已弃用的函数。 createCipher. The new function to use should be createCipheriv.

所以,为了使用新的createCipheriv,我使用文档写了一个新的加密和解密函数,类似于post中使用新的createCipheriv函数的函数。写完解密函数,报错

Error: Unsupported state or unable to authenticate data

在谷歌搜索那个问题后,它把我带到了这里 github post。简而言之,它说解密文件需要用密码生成的authTag。

我不知道这个 authTag 是什么,我认识的人也不知道。所以我开始用谷歌搜索它,它让我找到了这个 blogpost。它指出

The authTag is the message authentication code (MAC) calculated during the encryption.

这里 wikipedia article 关于什么是消息验证码。

所以。这是我的问题。消息验证码是否可以公开显示,还是需要像密码一样保密?


我的代码,不太相关,但可能会帮助某人使用 createCipheriv 和 createDecipheriv 创建加密和解密。

加密

const crypto = require('crypto');
const fs = require('fs');

// const iv = crypto.randomBytes(32).toString('hex');
// EDIT - based on @President James K. Polk. The initialization vector should be 12 bytes long
// const iv = crypto.randomBytes(6).toString('hex');
// EDIT - based on @dsprenkels. I misunderstood @President James K. Polk
const iv = crypto.randomBytes(12).toString('hex');
const privateKey = 'private key that is 32 byte long';
const cipher = crypto.createCipheriv('aes-256-gcm', privateKey, iv);

const filename = 'somefile.txt';
const encFilename = 'somefile.txt.enc';
const unencryptedInput = fs.createReadStream(filename);
const encryptedOutput = fs.createWriteStream(encFilename);
unencryptedInput.pipe(cipher).pipe(encryptedOutput);

encryptedOutput.on('finish', () => {
    const authTagAsHex = cipher.getAuthTag().toString('hex'); // <-- can this be public
    console.log(authTagAsHex);
});

解密

const crypto = require('crypto');
const fs = require('fs');

// const publicIV = 'same iv generated during encryption crypto.randomBytes(32).toString("hex")';
// EDIT - based on @President James K. Polk. The initialization vector should be 12 bytes long
// const publicIV = 'same iv generated during encryption crypto.randomBytes(6).toString("hex")';
// EDIT - based on @dsprenkels. I misunderstood @President James K. Polk
const publicIV = 'same iv generated during encryption crypto.randomBytes(12).toString("hex")';
const authTag = 'same authKey generated from cipher.getAuthTag().toString("hex")';
const privateKey = 'private key that is 32 byte long';
const decipher = crypto.createDecipheriv('aes-256-gcm', privateKey, publicIV);
decipher.setAuthTag(Buffer.from(authTag, 'hex'));

const filename = 'somefile.txt';
const encFilename = 'somefile.txt.enc';
const readStream = fs.createReadStream(encFilename);
const writeStream = fs.createWriteStream(filename);
readStream.pipe(decipher).pipe(writeStream);

是。 MAC被认为是public。

一般来说,消息验证码被认为是public。消息验证代码 验证 在您提供的密钥下的(加密)消息。换句话说,接收方使用它来检查密文在传输过程中是否没有改变。在您的情况下,只要密钥保密,攻击者就不会使用 MAC.

通常在存储密文时将MAC放在密文旁边(就像IV一样)。


顺便说一下,在您的情况下,您 随机生成 IV。这很好,但请注意,可以在同一密钥下安全加密的消息数量相当 small。如果 IV 用于多条消息(甚至一次!),则此方案的完整安全性就会崩溃。真的,你可能想要这个:

const iv = crypto.randomBytes(12);