使用密文Crypto ++ CBC AES加密存储IV

时间:2017-08-02 13:08:28

标签: c++ encryption aes crypto++ initialization-vector

我正在尝试使用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字节长,但在这里我完全迷失了,我不知道该怎么做。

2 个答案:

答案 0 :(得分:1)

  

使用密文Crypto ++ CBC AES加密存储IV

您使用的一些代码对我来说有点不寻常。我会选择一些东西并向您展示一些Crypto ++方法。

在开始之前,请先查看Crypto ++ wiki上的PipelinesPumping 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字节)和密文大小进行比较。如果它是平等的那么你应该没事。