【问题标题】:How to initialize AES-256 key with user defined password如何使用用户定义的密码初始化 AES-256 密钥
【发布时间】:2017-02-09 10:39:18
【问题描述】:

如果我想加密数据,使用 Crypto++ 库并使用短于 32 字节的用户定义密码?

现在我有以下代码:

byte passwordBytes[AES::MAX_KEYLENGTH];
byte ivBytes[AES::BLOCKSIZE];
std::string textToEncrypt("encryptMe");
std::string aesKey("passwordFromUser");
std::string ivText("Iv16BytesOfText...");

memset(passwordBytes, 0, sizeof(passwordBytes)); //fill with zeroes first
memcpy(passwordBytes, aesKey.data(), aesKey.size()); //fill with key data
memcpy(ivBytes, ivText.data(), CryptoPP::AES::BLOCKSIZE); //fill iv bytes

CTR_Mode<AES>::Encryption encryption;
encryption.SetKeyWithIV(passwordBytes, sizeof(passwordBytes), ivBytes);

StringSource encryptor(textToEncrypt, true,
    new StreamTransformationFilter(encryption,
            new StringSink(verschluesselterText)
        ,StreamTransformationFilter::NO_PADDING
    )
);

如您所见,aesKey 小于 32 字节。

要将完整的 32 字节应用于加密函数,我只需用零填充未使用的空间,但这对我来说似乎不是最好的解决方案。

我是否遗漏了有关创建 AES 密钥的内容?使用用户定义的密码?

我的第二个问题,如果用户选择的密码长于 32 字节怎么办?在我的情况下,密码会被截断,这对我来说听起来不对。

感谢您的帮助!

【问题讨论】:

  • 你是怎么解决这个问题的?

标签: c++ encryption aes crypto++


【解决方案1】:

如果我想加密数据,使用 Crypto++ 库并使用短于 32 字节的用户定义密码怎么办?

使用密钥派生函数 (KDF) 来消化密码。现代的是 Krawczyk 和 Eronen 的 HKDF,使用的是 Extract-then-Expand 模型。论文地址Cryptographic Extraction and Key Derivation: The HKDF Scheme

您也应该考虑将它用于 IV。与其派生 32 个字节 (AES::MAX_KEYLENGTH),不如派生 48 个字节 (AES::MAX_KEYLENGTH+AES::BLOCKSIZE)。然后,您设计中的 IV 可用于 KDF 的 salt 参数。

可能是这样的:

#include <iostream>
#include <string>
using namespace std;

#include "cryptlib.h"
#include "aes.h"
#include "sha.h"
#include "hkdf.h"
#include "modes.h"
#include "filters.h"
using namespace CryptoPP;

int main(int argc, char* argv[])
{
  SecByteBlock key(AES::MAX_KEYLENGTH+AES::BLOCKSIZE);
  string password("passwordFromUser"), iv("<random value>"), message("encryptMe");
  string encrypted, recovered;

  try
  {
    HKDF<SHA256> hkdf;
    hkdf.DeriveKey(key, key.size(), (const byte*)password.data(), password.size(), (const byte*)iv.data(), iv.size(), NULL, 0);

    ///////////////////////////////////////////////////////////////////////

    CTR_Mode<AES>::Encryption encryption;
    encryption.SetKeyWithIV(key, AES::MAX_KEYLENGTH, key+AES::MAX_KEYLENGTH);

    StringSource encryptor(message, true,
      new StreamTransformationFilter(encryption,
        new StringSink(encrypted))
    );

    ///////////////////////////////////////////////////////////////////////

    CTR_Mode<AES>::Decryption decryption;
    decryption.SetKeyWithIV(key, AES::MAX_KEYLENGTH, key+AES::MAX_KEYLENGTH);

    StringSource decryptor(encrypted, true,
      new StreamTransformationFilter(decryption,
        new StringSink(recovered))
    );

    cout << "Message: " << message << endl;
    cout << "Recovered: " << recovered << endl;
  }
  catch(const Exception& ex)
  {
    cerr << ex.what() << endl;
    return 1;
  }

  return 0;
}

使用上述加密方法时,您必须跟踪{iv,message} 对。需要 IV 来确保每条消息的唯一性,因为密码有效地修复了 AES 密钥。


如果用户选择的密码长于 32 字节怎么办?在我的情况下,密码会被截断,这对我来说听起来不对。

KDF 会为您处理。无论多少熵,它都会提取熵。


StringSource encryptor(textToEncrypt, true,
    new StreamTransformationFilter(encryption,
        new StringSink(verschluesselterText),
        StreamTransformationFilter::NO_PADDING
    )

无需指定填充模式。另请参阅BlockPaddingScheme 的文档。


您应该非常小心点击率等模式。 CTR 模式 xor 是带有纯文本的密钥流。如果有人在不同的消息中重复使用他们的密码,那么就有可能恢复导致明文恢复的密钥流。

如果 ivText 对于每条消息都是唯一的,那么您应该将它添加到您的 KDF 以确保每条消息的密钥流都是唯一的。添加 IV 作为 HKDF 的 salt 参数。这里,"unique" 的意思是如果我有一条消息"Hello World",那么每次我加密消息时,IV 都是不同的。

如果 IV 真的只是“Iv16BytesOfText...”(即,它是固定的),那么它就没有什么独特之处了。只需从用户密码中提取额外的 16 个字节。然后,为了避免密钥流异或攻击,请切换到 CBC 之类的模式。

最后,您可能应该使用 CCM、EAX 或 GCM 模式。现在,你只有保密。通常你也想要真实性。为了获得真实性,您通常选择Authenticated Encryption 操作模式。

【讨论】:

  • 谢谢,但是对于解密,我通过执行解密(加密(解密(enc_pass)))而不是仅仅执行解密(enc_pass)得到正确的值,知道为什么吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-10-15
  • 1970-01-01
  • 1970-01-01
  • 2017-10-28
  • 2015-03-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多