I needed to encrypt a string to AES/CBC. In order to be able to uncrypt it later I have to store the IV in the final result which must be a base64 string.
I manage to do that using this answer and the Crypto++ samples :
std::string String::AESEncryptString(std::string str, std::string key)
{
std::string encoded;
std::string b64Result = "";
AutoSeededRandomPool prng;
unsigned char iv[AES::BLOCKSIZE];
prng.GenerateBlock(iv, sizeof(iv));
StringSink output(encoded);
// Put the IV at the begining of the output
StringSource(iv, sizeof(iv), true,
new Redirector(output)
);
try {
CBC_Mode<AES>::Encryption encryptor((unsigned char *)key.c_str(), key.length(), iv);
StringSource s(str, true,
new StreamTransformationFilter(encryptor,
new Redirector(output), StreamTransformationFilter::PKCS_PADDING
)
);
// Convert to b64
StringSource (encoded, true,
new Base64Encoder(
new StringSink(b64Result),
false // do not append a newline
)
);
return b64Result;
} catch (const Exception& e) {
return "";
}
}
To decrypt the base64 string I extract the IV first then decrypt the rest of the datas :
std::string String::AESDecryptString(std::string str, std::string key)
{
unsigned char iv[AES::BLOCKSIZE];
std::string b64decoded;
std::string decoded;
try {
StringSource(str, true,
new Base64Decoder(
new StringSink(b64decoded)
)
);
StringSource ss(b64decoded, false);
// Get the IV
ArraySink ivSink(iv, sizeof(iv));
ss.Attach(new Redirector(ivSink));
ss.Pump(AES::BLOCKSIZE);
CBC_Mode<AES>::Decryption decryptor((unsigned char *)key.c_str(), key.length(), iv);
ByteQueue queue;
ss.Detach(
new StreamTransformationFilter(decryptor,
new Redirector(queue)
)
);
ss.PumpAll(); // Pump remainder bytes
StringSink decodedSink(decoded);
queue.TransferTo(decodedSink);
return decoded;
}
catch (const Exception& e) {
return "";
}
}
Everything is working fine, but as I'm just discovering Crypto++ and the pipelining paradigm, I feel that I may have done too many steps to achieve what I want.
Is there more concise or more efficient way of doing that ?