Storing the IV with the ciphertext Crypto++ CBC AES encryption

匆匆过客 提交于 2020-01-14 04:38:05

问题


I'm trying to encrypt(and decrypt after) a file using AES in CBC mode and Crypto++ library

Here's what I already did:

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);

To encrypt a file,I open it in binary mode,and dump the content to a string :

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();

Then,I encrypt this string using the key and iv previously generated:

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();

Now,i want to write the encrypted string to a file,and store the IV with it,since the iv doesn't need to be kept secret,ideally at the beginning or the end of the ciphertext

Here comes the problem : the IV is a byte array and the ciphertext is a string,do i need to convert one of the two to another type,or can i just do :

std::ofstream fdout(file_path2, std::ios::binary);
if (!fdout)
{
    std::cout << "error";
}
fdout << iv;
fdout << ciphertext;
fdout.close();

When i will try to decrypt this file,how can i extract the iv and ciphertext separately ? The IV is 16 Bytes long,but here i'm completely lost and i don't know how to do.


回答1:


Storing the IV with the ciphertext Crypto++ CBC AES encryption

Some of the code you are using is a bit unusual to me. I'll pick a couple things out and show you some of the Crypto++ ways.

Before you begin, take a look at Pipelines and Pumping Data on the Crypto++ wiki. Remember that data flows from sources to sinks. In between the data encounters filters which transform the 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();

A Crypto++ FileSource has a constructor that takes a std::istream. You could do something like the following. Also see FileSource on the Crypto++ wiki.

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 are for the FIPS DLL. You can use them without the DLL, but they exist for the DLL. Usually you use:

 CBC_Mode<AES>::Encryption encryptor;

Also, you usually want to avoid a confidentiality-only mode. Typically you want to use an Authenticated Encryption mode of operation. It provides confidentiality and authenticity.

Crypto++ provides CCM, EAX and GCM authenticated encryption modes of operation. OCB and EAX are very good choices. EAX mode is documented at EAX Mode on the Crypto++ wiki. OCB is not available at the moment. We are getting ready to check-in OCB mode.


Now,i want to write the encrypted string to a file,and store the IV with it,since the iv doesn't need to be kept secret,ideally at the beginning or the end of the ciphertext

Use something like the following. I did not compile it, so you will need to fix the typos.

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)));

When i will try to decrypt this file,how can i extract the iv and ciphertext separately ?

Use something like the following.

// 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

The encrypted data will be in a ByteQueue. It does not provide C++ iterator-like functionality, like a pointer and a size. To get the data out of the ByteQueue you transfer it or copy it to another filter or a sink:

SecByteBlock block(queue.MaxRetrievable());
ArraySink sink(block, block.size());
queue.TransferTo(sink);

You can get the data out of the ByteQueue and put it in astd::string with:

std::string recovered;
StringSink sink(recovered);
queue.TransferTo(sink);

And you can print the IV recovered from the file with:

HexEncoder encoder(new FileSink(std::cout));

std::cout << "IV: ";
encoder.Put(iv, iv.size());
encoder.MessageEnd();
std::cout << std::endl;



回答2:


The StringSink has the following documentation:

A StringSink serves as a destination for string data in the pipelining paradigm. The data can be binary or ASCII.

So although it is a string, it is internally an octet- or byte string. So yes, you can just save the IV and ciphertext that way.

But to be sure, just compare the length of the result with the IV size (16 bytes) and ciphertext size together. If it is equal then you should be fine.



来源:https://stackoverflow.com/questions/45461770/storing-the-iv-with-the-ciphertext-crypto-cbc-aes-encryption

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!