Я пытаюсь зашифровать (и расшифровать после) файл с использованием AES в режиме CBC и библиотеки 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 байт, но здесь я полностью потерян и не знаю, как это сделать.
Хранение IV с шифрованным текстом Crypto ++ CBC AES шифрование
Часть кода, который вы используете, немного необычна для меня. Я выберу пару вещей и покажу вам некоторые способы Crypto ++.
Прежде чем начать, взгляните на Трубопроводы а также Насосные данные на Crypto ++ вики. Помните, что данные поступают из источников в приемники. Между данными встречаются фильтры, которые преобразуют данные.
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();
Крипто ++ FileSource
имеет конструктор, который принимает std::istream
, Вы могли бы сделать что-то вроде следующего. Также см FileSource
на Crypto ++ вики.
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. Обычно вы используете:
CBC_Mode<AES>::Encryption encryptor;
Кроме того, вы обычно хотите избежать Конфиденциальность только Режим. Обычно вы хотите использовать Аутентифицированное шифрование режим работы. Это обеспечивает конфиденциальность и подлинность.
Crypto ++ обеспечивает режимы шифрования с аутентификацией CCM, EAX и GCM. OCB и EAX — очень хороший выбор. Режим EAX задокументирован в EAX Mode на Crypto ++ вики. 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;
StringSink
имеет следующую документацию:
StringSink
служит местом назначения для строковых данных в парадигме конвейерной обработки. Данные могут быть двоичными или ASCII.
Так что, хотя это строка, это внутренне октет- или байтовая строка. Так что да, вы можете просто сохранить IV и зашифрованный текст таким образом.
Но чтобы быть уверенным, просто сравните длину результата с размером IV (16 байт) и размером зашифрованного текста вместе. Если он равен, то у вас все будет хорошо.