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;
Aut
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;
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.