如何使用用户定义的密码

时间:2017-02-09 10:39:18

标签: c++ encryption aes crypto++

如果我想使用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字节怎么办?在我的情况下,密码将被截断,这听起来不对我。

感谢您的帮助!

1 个答案:

答案 0 :(得分:4)

  

如果我想使用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}对。由于密码有效地修复了AES密钥,因此需要IV以确保每条消息的唯一性。

  

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

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

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

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

你应该对CTR这样的模式非常小心。 CTR模式xor是具有纯文本的密钥流。如果有人在不同的邮件上重复使用他们的密码,那么就有可能恢复密钥流,从而导致明文恢复。

如果 ivText对于每封邮件都是唯一的,那么您应该将其添加到KDF以确保每条邮件的唯一密钥流。添加IV作为HKDF的salt参数。在这里,“unique”表示如果我有一条消息“Hello World”,那么每次加密消息时IV都不同。

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

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