【问题标题】:Is this encryption method secure?这种加密方法安全吗?
【发布时间】:2014-04-26 19:47:48
【问题描述】:

我使用 Crypto++ 开发了一个 C++ 应用程序来加密信息并将文件存储在硬盘驱动器中。我使用完整性字符串来检查用户输入的密码是否正确。你能告诉我实现是否生成了一个安全文件吗?我是密码学世界的新手,我用我读过的东西制作了这个程序。

string integrity = "ImGood"
string plaintext = integrity + string("some text");
byte password[pswd.length()]; // The password is filled somewhere else
byte salt[SALT_SIZE]; // SALT_SIZE is 32
byte key[CryptoPP::AES::MAX_KEYLENGTH];
byte iv[CryptoPP::AES::BLOCKSIZE];

CryptoPP::AutoSeededRandomPool rnd;
rnd.GenerateBlock(iv, CryptoPP::AES::BLOCKSIZE);
rnd.GenerateBlock(salt, SALT_SIZE);

CryptoPP::PKCS5_PBKDF2_HMAC<CryptoPP::SHA512> gen;
gen.DeriveKey(key, CryptoPP::AES::MAX_KEYLENGTH, 32,
              password, pswd.length(),
              salt, SALT_SIZE,
              256);

CryptoPP::StringSink* sink = new CryptoPP::StringSink(cipher);
CryptoPP::Base64Encoder* base64_enc = new CryptoPP::Base64Encoder(sink);
CryptoPP::CFB_Mode<CryptoPP::AES>::Encryption cfb_encryption(key, CryptoPP::AES::MAX_KEYLENGTH, iv);
CryptoPP::StreamTransformationFilter* aes_enc = new CryptoPP::StreamTransformationFilter(cfb_encryption, base64_enc);
CryptoPP::StringSource source(plaintext, true, aes_enc);

sstream out;
out << iv << salt << cipher;

然后将字符串流“out”中的信息写入文件。另一件事是我不知道派生函数中的“目的”参数是什么意思,我猜它是密钥的所需长度所以我输入了 32,但我不确定也找不到Crypto++ 手册中有关它的任何内容。

感谢指出的任何意见、建议或错误。

非常感谢您。

【问题讨论】:

  • 乍一看,你的“完整性”检查完全被破坏了(它只保护了每个 AFAICT 块的前六个字节的完整性,即使这样也只是很弱)。使用经过身份验证的密码模式(例如,GCM、EAX、CCFB)或派生第二个密钥(这就是“目的”参数的用途;允许您从相同的输入生成多个用于不同目的的密钥)并使用它来计算密文和 IV 上的 HMAC。
  • 非常感谢斯蒂芬。我认为如果完整性字符串被正确解密,那么整个消息就意味着密码是正确的。但我会阅读身份验证和您提到的那些模式。最后,如果我将目的设置为 32,代码会生成密钥编号 32?再次感谢。
  • purpose 参数的值是完全任意的,只要它在你使用它的任何上下文中都是一致的。我很好奇:究竟是什么让你相信你的方法将确保密文的完整性?
  • 我想我错误地解释了完整性字符串的含义。我用它来查看用户是否写了正确的密码,如果密码正确,那么解密后的明文将是“ImGood”+“要安全的文本”。如果与该字符串对应的字节不等于“ImGood”,则密码错误。目前我不知道验证密码的方法,但我仍在阅读它。感谢您花时间回答。
  • 这个问题似乎跑题了,因为它要求进行代码审查,应该移到 codereview.stackexchange.com 而不是这里。

标签: aes pbkdf2 c++


【解决方案1】:

只有在您定义“安全”的含义时,文件才能“安全”。

通常,您会对两个属性感兴趣:

  • 机密性:被加密的数据对攻击者来说是不可读的;揭示明文数据需要了解特定的秘密。

  • 完整性:应可靠地检测到数据的任何更改;攻击者不能以任何方式(甚至“盲目地”)修改数据,而不会被解密数据的人注意到。

您的代码显然(在某种程度上)满足机密性,但不满足完整性。您的名为“integrity”的字符串用词不当:它不是完整性检查。它的作用显然是检测意外密码错误,而不是攻击;因此,如果将该字符串称为passwordVerifier,则不会那么混乱。攻击者可以更改前 48 位以外的任何位,而解密过程不会注意到任何事情。

添加完整性(真正的东西)需要使用MAC。安全地结合加密和 MAC 是subject to subtleties;因此,建议对加密和 MAC 使用authenticated encryption mode,它既能做到,又能安全地做到这一点(即,该特定组合已被成群的密码学家明确审查过)。通常推荐的AE模式包括GCMEAX

需要注意的重要一点是,在完整性很重要的情况下,数据在经过验证之前无法处理。这对大文件有影响:如果你的大文件是用单个 MAC 装饰的(无论是“手动”还是作为 AE 模式的一部分),那么在开始对纯文本数据执行任何操作之前,您必须首先验证完整的文件。这不适用于流式处理(例如,如果播放巨大的视频)。一种解决方法是将数据分成单独的块,每个块都有自己的 MAC,但是必须注意块的顺序(攻击者可能会尝试删除、复制或重新排序块):事情变得复杂。一般来说,复杂性不利于安全性。

在某些情况下,完整性并不重要。例如,如果您的攻击模型是“攻击者窃取笔记本电脑”,那么您只需要关心机密性。但是,如果攻击模型是“攻击者偷走了笔记本电脑,修改了一些文件,然后在我不注意的情况下将其放回我的手提箱”,那么完整性很重要:攻击者可以在文件中植入修改,并推断出部分内容当您下次访问文件时,根据您的外部行为对其进行保密。

为了保密,你使用CFB,这有点老套,但没有错。对于密码到密钥的转换,您使用 PBKDF2,这很好;但是,迭代计数非常低:您使用 256。典型值为 20000 或更多。 theory 是您应该进行实际性能测量,将此计数设置为您可以容忍的最高值:较高的值意味着您和攻击者的处理速度较慢,因此您应该加快速度(取决于你的耐心)。


强制警告:您正在定义自己的加密货币,这是一条充满危险的道路。大多数这样做的人都会产生薄弱的系统,其中包括训练有素的密码学家;事实上,成为一名训练有素的密码学家并不意味着您知道如何定义安全协议,而是您比定义自己的协议更了解。因此,强烈建议您依赖现有的协议(或格式),而不是自己制作。我建议OpenPGP,使用(例如)GnuPG 作为支持库。即使您出于某种原因(例如许可证问题)需要重新实现该格式,使用标准格式仍然是一个好主意:它可以避免引入弱点,促进互操作性,并且可以针对现有系统进行测试。

【讨论】:

  • 非常感谢您花时间写下这样的答案。 Stephen Touset 还建议使用一种身份验证模式,因此我将 CFB 实现替换为使用 EAX 的一种,现在 Crypto++ 进行身份验证,如果密码不正确或完整性受到损害,则会失败。我现在正在阅读有关 OpenPGP 和 GnuPG 的信息。再次感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-05-03
  • 1970-01-01
  • 2017-08-23
  • 1970-01-01
  • 2011-03-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多