【问题标题】:Can the value from node crypto.createCipheriv('aes-256-gcm', ...).getAuthKey() be public?节点 crypto.createCipheriv('aes-256-gcm', ...).getAuthKey() 的值可以公开吗?
【发布时间】:2020-06-18 23:41:14
【问题描述】:

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

TL;DR

消息验证码能否公开可见,还是需要像密码一样保密?

一些背景,我正在尝试加密文件。我找到了这个帮助我入门的 stackoverflow 问题和答案。 https://stackoverflow.com/a/27345933/11070228

在对 nodejs 文档进行了一些研究后,我发现答案使用了一个已弃用的函数。 createCipher。要使用的新函数应该是createCipheriv

所以,为了使用新的 createCipheriv,我使用文档编写了一个新的加密和解密函数,类似于帖子中使用新的 createCipheriv 函数的那个​​。写完解密函数后报错是

错误:不支持的状态或无法验证数据

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

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

authTag 是在加密过程中计算的消息验证码 (MAC)。

这里有一个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);

【问题讨论】:

  • 身份验证标签不是秘密,通常放置在加密消息的某个位置,通常位于末尾。 IV(通常称为 GCM 模式的“nonce”)应该是 12 个字节,也不是秘密,通常也放在与加密消息相同的流中,通常放在开头。
  • 太棒了,您的回复非常有帮助,非常感谢!我将代码更新为仅使用 12 字节的初始化向量。

标签: node.js encryption cryptography aes-gcm message-authentication-code


【解决方案1】:

是的。 MAC 被认为是公开的。

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

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


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

const iv = crypto.randomBytes(12);

【讨论】:

  • 太棒了!谢谢你抓住那个。我更新了上面的代码以反映这种变化。感谢您的帮助!
猜你喜欢
  • 2021-09-23
  • 2018-10-29
  • 2021-04-12
  • 2012-03-25
  • 2021-06-04
  • 2015-09-23
  • 2019-09-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多