如果我想使用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字节怎么办?在我的情况下,密码将被截断,这听起来不对我。
感谢您的帮助!
答案 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操作模式。