Текст шифра не кратен размеру блока - PullRequest
0 голосов
/ 08 сентября 2018

Я написал простую программу шифрования AES для фрагмента текста, и всякий раз, когда я пытаюсь расшифровать текст, мне выдается ошибка;

StreamTransformationFilter: длина зашифрованного текста не кратнаразмер блока

Это мой метод шифрования;

c_crypto::cryptkey aes_key;

c_crypto::make_keys(aes_key);

std::string testmessage = "test";

c_crypto::encrypt_buffer(testmessage, aes_key.enc_key, aes_key.enc_iv);
c_crypto::decrypt_buffer(testmessage, aes_key.enc_key, aes_key.enc_iv);

std::cout << testmessage << std::endl;

структура cryptkey;

struct cryptkey {
    unsigned char enc_key[AES_KEY_SIZE];
    unsigned char enc_iv[AES_KEY_SIZE];
};

AES_KEY_SIZE = 16

Функция make_keys() просто вызывает;

void c_crypto::random_bytes(const int &amount, unsigned char *result) {
    CryptoPP::AutoSeededRandomPool rng;
    rng.GenerateBlock(result, amount);
}

С параметрами AES_KEY_SIZE или cryptkey.enc_iv или cryptkey.enc_key

В функции дешифрования;

void c_crypto::decrypt_buffer(std::string &input, unsigned char key[AES_KEY_SIZE], unsigned char iv[AES_KEY_SIZE]) {
    input = base64_decode(input);

    CryptoPP::AES::Decryption aesDecryption(key, AES_KEY_SIZE);
    CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, iv);

    CryptoPP::StreamTransformationFilter stfDecryptor(cbcDecryption, new CryptoPP::StringSink(input));
    stfDecryptor.Put(reinterpret_cast<const unsigned char*>(input.c_str()), input.size());
    stfDecryptor.MessageEnd();
}

Ошибка генерируется в stfDecryptor.MessageEnd ();

Вот функция шифрования, если она используется;

void c_crypto::encrypt_buffer(std::string &input, unsigned char key[AES_KEY_SIZE], unsigned char iv[AES_KEY_SIZE]) {
    CryptoPP::AES::Encryption aesEncryption(key, AES_KEY_SIZE);
    CryptoPP::CBC_Mode_ExternalCipher::Encryption cbcEncryption(aesEncryption, iv);

    CryptoPP::StreamTransformationFilter stfEncryptor(cbcEncryption, new CryptoPP::StringSink(input));
    stfEncryptor.Put(reinterpret_cast<const unsigned char*>(input.c_str()), input.length());
    stfEncryptor.MessageEnd();

    input = base64_encode(input);
}

РЕДАКТИРОВАТЬ:

ВотМои функции декодирования и кодирования:

std::string c_crypto::base64_encode(std::string &input) {
    std::string result;
    CryptoPP::StringSource(input, true, new CryptoPP::Base64Encoder(new CryptoPP::StringSink(result)));
    return result;
}

std::string c_crypto::base64_decode(std::string &input) {
    std::string result;
    CryptoPP::StringSource(input, true, new CryptoPP::Base64Decoder(new CryptoPP::StringSink(result)));
    std::cout << (input.size() % 16 == 0) << std::endl;
    return result;
}

1 Ответ

0 голосов
/ 09 сентября 2018
void c_crypto::encrypt_buffer(std::string &input, unsigned char key[AES_KEY_SIZE], unsigned char iv[AES_KEY_SIZE]) {
    CryptoPP::AES::Encryption aesEncryption(key, AES_KEY_SIZE);
    CryptoPP::CBC_Mode_ExternalCipher::Encryption cbcEncryption(aesEncryption, iv);

    CryptoPP::StreamTransformationFilter stfEncryptor(cbcEncryption, new CryptoPP::StringSink(input));
    stfEncryptor.Put(reinterpret_cast<const unsigned char*>(input.c_str()), input.length());
    stfEncryptor.MessageEnd();

    input = base64_encode(input);
}

Не используйте ту же строку в качестве источника и приемника, как вы. Вместо этого используйте отдельный временный.

Большинство (всех?) Блочных шифров могут взять 16-байтовый блок и зашифровать или расшифровать на месте. Однако, в случае строки, строка as изменяет базовые изменения размещения. Когда изменяется распределение, изменяется базовый указатель.

Может быть, использовать что-то вроде следующего. Временные потоки обходят проблемы использования строки как источника и приемника. Это также обеспечивает лучшую безопасность исключений. Если что-то идет в сторону, ваш input остается без изменений.

void c_crypto::encrypt_buffer(std::string &input, unsigned char key[AES_KEY_SIZE], unsigned char iv[AES_KEY_SIZE]) {
    AES::Encryption aesEncryption(key, AES_KEY_SIZE);
    CBC_Mode_ExternalCipher::Encryption cbcEncryption(aesEncryption, iv);

    std::string encrypted; encrypted.reserve(input.size()+16);
    StreamTransformationFilter stfEncryptor(cbcEncryption, new StringSink(encrypted));
    stfEncryptor.Put(reinterpret_cast<const unsigned char*>(input.c_str()), input.length());
    stfEncryptor.MessageEnd();

    std::string encoded = base64_encode(encrypted);
    std::swap(encoded, input);
}

void c_crypto::decrypt_buffer(std::string &input, unsigned char key[AES_KEY_SIZE], unsigned char iv[AES_KEY_SIZE]) {
    std::string decoded = base64_decode(input);
    std::string decrypted; decrypted.reserve(decoded.size());

    AES::Decryption aesDecryption(key, AES_KEY_SIZE);
    CBC_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, iv);

    StreamTransformationFilter stfDecryptor(cbcDecryption, new StringSink(decrypted));
    stfDecryptor.Put(reinterpret_cast<const unsigned char*>(decoded.c_str()), decoded.size());
    stfDecryptor.MessageEnd();

    std::swap(decrypted, input);
}

Использование буфера в качестве входного / выходного параметра работает нормально. В этом примере указатель не будет частично меняться в процессе:

byte buff[16] = {0};
CBC_Mode<AES>::Encryption enc(key, 16, iv);
enc.ProcessBlock(buff);

Однако ваш вариант использования немного сложнее, потому что зашифрованный текст добавляется к строке input посредством StringSink(input), что неизбежно приводит к росту input и делает недействительными используемые итераторы для передать данные в шифратор с помощью StringSource(input, ...).

Если интересно, ProcessBlock и ProcessString - это низкоуровневые функции-члены, используемые для преобразования данных. Фильтры типа StreamTransformationFilter просто звонят вам за это. Там нет магии или вреда. Это просто высокоуровневый способ обработки данных. Также см. BlockTransformation в руководстве по Doxygen.


Что касается заполнения, вы использовали:

StreamTransformationFilter stfEncryptor(cbcEncryption, new StringSink(input));

Что на самом деле это StreamTransformationFilter конструктор:

StreamTransformationFilter (StreamTransformation &c, BufferedTransformation *attachment=NULL, BlockPaddingScheme padding=DEFAULT_PADDING)

DEFAULT_PADDING - это одна из двух вещей. Для режимов, которым требуется заполнение, таких как ECB и CBC, это заполнение PKCS # 7 (PKCS_PADDING). Для режимов, которым не требуется заполнение, например, CTR, это не заполнение (NO_PADDING).

Обычно вы должны использовать значение по умолчанию, когда речь идет о схемах заполнения.

...