Хранение IV с шифрованным текстом Crypto++ CBC AES шифрование

Я пытаюсь зашифровать (и расшифровать после) файл с использованием 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 байт, но здесь я полностью потерян и не знаю, как это сделать.

2 ответа

Хранение 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 в вики 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 байт) и размером зашифрованного текста вместе. Если он равен, то у вас все будет хорошо.

Другие вопросы по тегам