我正在尝试使用CBC模式下的AES和Crypto ++库加密(并解密)文件
这就是我已经做过的事情:
using namespace CryptoPP;
AutoSeededRandomPool rnd;
//generating the key and iv
SecByteBlock key(AES::MAX_KEYLENGTH);
rnd.GenerateBlock(key, key.size());
byte iv[AES::BLOCKSIZE];
rnd.GenerateBlock(iv, AES::BLOCKSIZE);
要加密文件,我以二进制模式打开它,并将内容转储到字符串:
std::ifstream fin(file_path, std::ios::binary);
if (!fin)
{
std::cout << "error";
}
std::ostringstream ostrm;
ostrm << fin.rdbuf();
std::string plaintext(ostrm.str());
fin.close();
然后,我使用以前生成的密钥和iv加密此字符串:
std::string ciphertext;
AES::Encryption aesEncryption(key, CryptoPP::AES::MAX_KEYLENGTH);
CBC_Mode_ExternalCipher::Encryption cbcEncryption(aesEncryption, iv);
StreamTransformationFilter stfEncryptor(cbcEncryption, new CryptoPP::StringSink(ciphertext));
stfEncryptor.Put(reinterpret_cast<const unsigned char*>(plaintext.c_str()), plaintext.length() + 1);
stfEncryptor.MessageEnd();
现在,我想将加密的字符串写入文件,并将IV存储在文件中,因为iv不需要保密,理想情况是在密文的开头或结尾
问题出现了:IV是一个字节数组,密文是一个字符串,我需要将其中一个转换为另一个类型,或者我可以这样做:
std::ofstream fdout(file_path2, std::ios::binary);
if (!fdout)
{
std::cout << "error";
}
fdout << iv;
fdout << ciphertext;
fdout.close();
当我尝试解密此文件时,如何单独提取iv和密文? IV是16字节长,但在这里我完全迷失了,我不知道该怎么做。
答案 0 :(得分:1)
使用密文Crypto ++ CBC AES加密存储IV
您使用的一些代码对我来说有点不寻常。我会选择一些东西并向您展示一些Crypto ++方法。
在开始之前,请先查看Crypto ++ wiki上的Pipelines和Pumping Data。请记住,数据从源流向汇。在数据之间遇到转换数据的过滤器。
std::ifstream fin(file_path, std::ios::binary);
if (!fin)
{
std::cout << "error";
}
std::ostringstream ostrm;
ostrm << fin.rdbuf();
std::string plaintext(ostrm.str());
fin.close();
Crypto ++ FileSource
有一个带std::istream
的构造函数。您可以执行以下操作。另请参阅Crypto ++ wiki上的FileSource
。
std::ifstream fin(file_path, std::ios::binary);
FileSource source(fin, true /*pump all*/, NULLPTR);
...
AES::Encryption aesEncryption(key, CryptoPP::AES::MAX_KEYLENGTH); CBC_Mode_ExternalCipher::Encryption cbcEncryption(aesEncryption, iv);
ExternalCipher
用于FIPS DLL。您可以在没有DLL的情况下使用它们,但它们存在于DLL中。通常你使用:
CBC_Mode<AES>::Encryption encryptor;
此外,您通常希望避免使用机密性模式。通常,您希望使用Authenticated Encryption操作模式。它提供机密性和真实性。
Crypto ++提供CCM,EAX和GCM认证加密操作模式。 OCB和EAX是非常好的选择。在Crypto ++ wiki上的EAX Mode记录了EAX模式。 OCB暂时无法使用。我们正准备登记入住OCB模式。
现在,我想将加密的字符串写入文件,并将IV存储在文件中,因为iv不需要保密,理想情况是在密文的开头或结尾
使用以下内容。我没有编译它,所以你需要修复拼写错误。
AutoSeededRandomPool prng;
SecByteBlock key(AES::MAXIMUM_KEYLENGTH), iv(AES::BLOCKSIZE);
RandomNumberSource rs1(prng, AES::MAXIMUM_KEYLENGTH, new ArraySink(key, key.size()));
RandomNumberSource rs2(prng, AES::BLOCKSIZE, new ArraySink(iv, iv.size()));
HexEncoder encoder(new FileSink(std::cout));
std::cout << "Key: ";
encoder.Put(key, key.size());
encoder.MessageEnd();
std::cout << std::endl;
std::cout << "IV: ";
encoder.Put(iv, iv.size());
encoder.MessageEnd();
std::cout << std::endl;
EAX<AES>::Encryption encryptor;
encryptor.SetKeyWithIV(key, key.size(), iv, iv.size());
// Plaintext message
std::string message;
// Output file
FileSink file("message.enc");
// Source wrappers
ArraySource as(iv, iv.size(), true,
new Redirector(file));
// Source wrapper
StringSource ss(message, true,
new StreamTransformationFilter(encryptor,
new Redirector(file)));
当我尝试解密此文件时,如何单独提取iv和密文?
使用以下内容。
// Key is from previous example. It cannot change
SecByteBlock key(AES::MAXIMUM_KEYLENGTH), iv(AES::BLOCKSIZE);
FileSource fs("message.enc", false /* DO NOT Pump All */);
// Attach new filter
ArraySink as(iv, iv.size());
fs.Attach(new Redirector(as));
fs.Pump(AES::BLOCKSIZE); // Pump first 16 bytes
EAX<AES>::Decryption decryptor;
decryptor.SetKeyWithIV(key, key.size(), iv, iv.size());
// Detach previously attached filter, attach new filter
ByteQueue queue;
fs.Detach(new StreamTransformationFilter(decryptor, new Redirector(queue)));
fs.PumpAll(); // Pump remainder of bytes
加密数据将位于ByteQueue
。 不提供类似C ++迭代器的功能,如指针和大小。要从ByteQueue
中获取数据,请将其传输或复制到另一个过滤器或接收器:
SecByteBlock block(queue.MaxRetrievable());
ArraySink sink(block, block.size());
queue.TransferTo(sink);
您可以从ByteQueue
中获取数据并将其放在std::string
中:
std::string recovered;
StringSink sink(recovered);
queue.TransferTo(sink);
您可以打印从文件中恢复的IV:
HexEncoder encoder(new FileSink(std::cout));
std::cout << "IV: ";
encoder.Put(iv, iv.size());
encoder.MessageEnd();
std::cout << std::endl;
答案 1 :(得分:0)
StringSink
包含以下文档:
StringSink
在流水线操作范例中充当字符串数据的目标。 数据可以是二进制或ASCII。
因此虽然它是一个字符串,但它内部是八位字节或字节字符串。所以是的,你可以用这种方式保存IV和密文。
但可以肯定的是,只需将结果的长度与IV大小(16字节)和密文大小进行比较。如果它是平等的那么你应该没事。