I'm writing an AES key and iv to the file with crypto++ by using following code:
// write the key:
SecByteBlock key(AES::DEFAULT_KEYLENGTH);
SecByteBlock iv(AES::BLOCKSIZE);
string file = m_file_name + ".key";
FileSink* key_out = new FileSink(file.c_str());
Base64Encoder base64_key_enc(key_out);
base64_key_enc.Put(key.BytePtr(), key.size());
base64_key_enc.MessageEnd();
base64_key_enc.Put(iv.BytePtr(), iv.size());
base64_key_enc.MessageEnd();
and to read a the key and iv from the file back I use following:
// read key
string file = m_file_name + ".key";
SecByteBlock key(AES::DEFAULT_KEYLENGTH);
ArraySink* arr_key_in = new ArraySink(key, key.size());
Base64Decoder* base64_key_dec = new Base64Decoder(arr_key_in);
FileSource source(file.c_str(), false, base64_key_dec);
source.PumpMessages(1); // read only the key
// read iv
SecByteBlock iv(AES::BLOCKSIZE);
ArraySink* arr_iv_in = new ArraySink(iv, iv.size());
base64_key_dec->Detach(arr_iv_in);
source.PumpAll(); // read the rest (the iv)
Problem is that after reading a file the key is correct but iv is not, so i wornder what could be wrong with my sintax?
the contents of a key file are base64 hex encoded and it looks like so:
2Gnh3TbAJeQPmza9FKdqNg== FowuKut3pBl7g0Or+4FJUg==
the == means end of the message/key... First on is the key, while the other one is the iv, the above code does not read the iv from the file properly.
What is wrong with my code?
Problem is that after reading a file the key is correct but iv is not, so i wonder what could be wrong with my syntax?
I think the problem is the library does not understand your notion of a message, so it does not know how to stream the message. I suspect its reading everything in one shot.
You might be able to correct it with source.Pump(AES::DEFAULT_KEYLENGTH * 4 / 3)
. I think that's correct, to compensate for the base encoding expansion. Or, you could read Base64 concatenated strings, split on the space, and then process the two messages separately.
Or you could try the following. Its not much different than yours, except it writes a length prefix so the Key and IV can be easily delineated and recovered.
A typical run looks like:
$ ./cryptopp-test.exe
Key 1: 434477042E70083441961B2F98A9FB1C
IV 1: 73A06DA15676B5AFB80C1741B3651982
Encoded: ABBDRHcELnAINEGWGy+YqfscABBzoG2hVna1r7gMF0GzZRmC
Key 2: 434477042E70083441961B2F98A9FB1C
IV 2: 73A06DA15676B5AFB80C1741B3651982
HexEncoder hex;
string filename = "test.txt", r1, r2, r3, r4, r5;
Base64Encoder encoder;
AlgorithmParameters params = MakeParameters(Pad(), false)(InsertLineBreaks(), false);
encoder.Initialize(params);
SecByteBlock k1(AES::DEFAULT_KEYLENGTH), v1(AES::BLOCKSIZE);
// Generate random key and iv
OS_GenerateRandomBlock(false, k1, k1.size());
OS_GenerateRandomBlock(false, v1, v1.size());
// Print key
hex.Detach(new StringSink(r1));
hex.Put(k1, k1.size());
hex.MessageEnd();
cout << "Key 1: " << r1 << endl;
// Print iv
hex.Detach(new StringSink(r2));
hex.Put(v1, v1.size());
hex.MessageEnd();
cout << "IV 1: " << r2 << endl;
// Write key
encoder.PutWord16(static_cast<word16>(k1.size()), BIG_ENDIAN_ORDER);
encoder.Put(k1.data(), k1.size());
// Write iv
encoder.PutWord16(static_cast<word16>(v1.size()), BIG_ENDIAN_ORDER);
encoder.Put(v1.data(), v1.size());
encoder.MessageEnd();
// Save it
FileSink f1(filename.c_str(), true);
encoder.CopyTo(f1);
f1.MessageEnd();
// Print Base64 encoded
StringSink ss(r3);
encoder.TransferTo(ss);
ss.MessageEnd();
cout << "Encoded: " << r3 << endl;
// Read file
ByteQueue queue;
FileSource f2(filename.c_str(), false, new Base64Decoder(new Redirector(queue)));
f2.PumpAll();
// Read key and iv
SecByteBlock k2, v2;
word16 size = 0;
// Read key
queue.GetWord16(size, BIG_ENDIAN_ORDER);
k2.resize(size);
queue.Get(k2, k2.size());
// Read iv
queue.GetWord16(size, BIG_ENDIAN_ORDER);
v2.resize(size);
queue.Get(v2, v2.size());
// Print key
hex.Detach(new StringSink(r4));
hex.Put(k2, k2.size());
hex.MessageEnd();
cout << "Key 2: " << r4 << endl;
// Print iv
hex.Detach(new StringSink(r5));
hex.Put(v2, v2.size());
hex.MessageEnd();
cout << "IV 2: " << r5 << endl;
The reference for the MakeParameters
trick to remove padding and line breaks is at NameValuePairs on the Crypto++ wiki.
The reference for the OS_GenerateRandomBlock
is at RandomNumberGenerator on the Crypto++ wiki.
And you can find HexEncoder, Base64Encoder, Redirector and friends on the Crypto++ wiki.
Also, AES::DEFAULT_KEYLENGTH
is 16 bytes (AES-128). That's usually enough for most people's needs. You could bump that to 32, if desired (AES-256).