StreamTransformationFilter: обнаружен неправильный отступ блока PKCS # 7 с использованием расшифровки AES - PullRequest
1 голос
/ 09 июля 2019

Я пытаюсь выполнить расшифровку AES с помощью библиотеки crypto++. У меня есть зашифрованный файл, чьи первые 8 байтов соответствуют длине файла, последующие 16 байтов - это вектор инициализации, а оставшиеся - интересующие данные. У меня также есть строковое представление моего ключа (который я хэширую с помощью SHA256)

Я получаю следующую ошибку при попытке выполнить расшифровку AES: StreamTransformationFilter: invalid PKCS #7 block padding found

Я использую следующий код C ++:

std::string keyStr = "my_key";
std::string infilePath = "my/file/path";


CryptoPP::SHA256 hash;
unsigned char digest[CryptoPP::SHA256::DIGESTSIZE];

hash.CalculateDigest( digest, reinterpret_cast<const unsigned char*>(&keyStr[0]), keyStr.length() );
auto key = CryptoPP::SecByteBlock(digest, CryptoPP::SHA256::DIGESTSIZE);

std::ifstream fin(infilePath, std::ifstream::binary);

// First 8 bytes is the file size
std::vector<char> fileSizeVec(8);
fin.read(fileSizeVec.data(), fileSizeVec.size());

// Read the next 16 bytes to get the initialization vector
std::vector<char> ivBuffer(16);
fin.read(ivBuffer.data(), ivBuffer.size());
CryptoPP::SecByteBlock iv(reinterpret_cast<const unsigned char*>(ivBuffer.data()), ivBuffer.size());

// Create a CBC decryptor
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption decryption;
decryption.SetKeyWithIV(key, sizeof(key), iv);

CryptoPP::StreamTransformationFilter decryptor(decryption);

std::vector<char> buffer(CHUNK_SIZE, 0);

while(fin.read(buffer.data(), buffer.size())) {
    CryptoPP::SecByteBlock tmp(reinterpret_cast<const unsigned char*>(buffer.data()), buffer.size());
    decryptor.Put(tmp, tmp.size());
        }

decryptor.MessageEnd();
size_t retSize = decryptor.MaxRetrievable();

std::vector<char> decryptedBuff;
decryptedBuff.resize(retSize);

decryptor.Get(reinterpret_cast<CryptoPP::byte*>(decryptedBuff.data()), decryptedBuff.size());

Я не уверен, что дает мне ошибку. Я работаю над следующим кодом Python. Когда я запускаю код Python с тем же входным файлом, он успешно расшифровывает файл.

def decrypt_file(in_filename, out_filename=None):
    key = hashlib.sha256(PASSWORD).digest()
    """loads and returns the embedded model"""
    chunksize = 24 * 1024
    if not out_filename:
        out_filename = os.path.splitext(in_filename)[0]

    with open(in_filename, 'rb') as infile:
        # get the initial 8 bytes with file size
        tmp = infile.read(8)
        iv = infile.read(16)
        decryptor = AES.new(key, AES.MODE_CBC, iv)
        string = b''
        # with open(out_filename, 'wb') as outfile:
        while True:
            chunk = infile.read(chunksize)
            if len(chunk) == 0:
                break
            string += decryptor.decrypt(chunk)
    return string

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

Заранее спасибо!

Edit: Похоже, я не читал входной файл до самого конца (так как длина последнего блока меньше CHUNK_SIZE). Следующий код теперь читает весь файл, однако я все еще получаю ту же проблему. Я также подтвердил, что IV и key точно совпадают с теми, что получены из кода Python.

 // Get the length of the file in bytes
fin.seekg (0, fin.end);
size_t fileLen = fin.tellg();
fin.seekg (0, fin.beg);

std::vector<char> buffer(CHUNK_SIZE, 0);
size_t readSize = CHUNK_SIZE;

while(fin.read(buffer.data(), readSize)) {
     CryptoPP::SecByteBlock tmp(reinterpret_cast<const unsigned char*>(buffer.data()), CHUNK_SIZE);
     decryptor.Put(tmp, tmp.size());

      std::fill(buffer.begin(), buffer.end(), 0);

      size_t bytesReamining = fileLen - fin.tellg();
      readSize = CHUNK_SIZE < bytesReamining ? CHUNK_SIZE : bytesReamining;

       if (!readSize)
            break;
        }
}

Обратите внимание, что я пробовал эту строку как CryptoPP::SecByteBlock tmp(reinterpret_cast<const unsigned char*>(buffer.data()), CHUNK_SIZE); и CryptoPP::SecByteBlock tmp(reinterpret_cast<const unsigned char*>(buffer.data()), readSize); (используя CHUNK_SIZE прокладки с 0)

Ответы [ 2 ]

1 голос
/ 11 июля 2019

У меня есть зашифрованный файл, первые 8 байтов которого представляют собой длину файла, последующие 16 байтов - это вектор инициализации, а оставшиеся данные - интересующие данные ...

Я думаюЯ просто перейду к поиску и покажу вам более простой способ работы с библиотекой Crypto ++.Ключ и iv жестко закодированы для упрощения кода.Деривация для примера не нужна.Кстати, если у Python он есть, вы должны рассмотреть возможность использования HKDF для получения ключа AES и iv.HKDF обладает доказуемыми свойствами безопасности.

Crypto ++ обрабатывает фрагменты для вас.Вам не нужно явно выполнять это;см. Насосные данные на вики-сайте Crypto ++.

Я считаю, что в коде Python есть потенциальный оракул заполнения из-за использования режима CBC без MAC.Возможно, вы захотите добавить MAC или использовать режим работы Authenticated Encryption .

#include "cryptlib.h"
#include "filters.h"
#include "osrng.h"
#include "modes.h"
#include "files.h"
#include "aes.h"
#include "hex.h"

#include <string>
#include <iostream>

const std::string infilePath = "test.dat";

int main(int argc, char* argv[])
{
    using namespace CryptoPP;

    const byte key[16] = {
        1,2,3,4, 1,2,3,4, 1,2,3,4, 1,2,3,4
    };
    const byte iv[16] = {
        8,7,6,5, 8,7,6,5, 8,7,6,5, 8,7,6,5
    };
    const byte data[] = // 70 characters
        "Now is the time for all good men to come to the aide of their country.";

    HexEncoder encoder(new FileSink(std::cout));
    std::string message;

    // Show parameters
    {        
        std::cout << "Key: ";
        StringSource(key, 16, true, new Redirector(encoder));
        std::cout << std::endl;

        std::cout << "IV: ";
        StringSource(iv, 16, true, new Redirector(encoder));
        std::cout << std::endl;

        std::cout << "Data: ";
        StringSource(data, 70, true, new Redirector(encoder));
        std::cout << std::endl;
    }

    // Write sample data
    {
        FileSink outFile(infilePath.c_str());
        word64 length = 8+16+70;

        outFile.PutWord64(length, BIG_ENDIAN_ORDER);
        outFile.Put(iv, 16);

        CBC_Mode<AES>::Encryption enc;
        enc.SetKeyWithIV(key, 16, iv, 16);

        StringSource(data, 70, true, new StreamTransformationFilter(enc, new Redirector(outFile)));
    }

    // Read sample data
    {
        FileSource inFile(infilePath.c_str(), true /*pumpAll*/);

        word64 read, l;
        read = inFile.GetWord64(l, BIG_ENDIAN_ORDER);
        if (read != 8)
            throw std::runtime_error("Failed to read length");

        SecByteBlock v(16);
        read = inFile.Get(v, 16);
        if (read != 16)
            throw std::runtime_error("Failed to read iv");

        CBC_Mode<AES>::Decryption dec;
        dec.SetKeyWithIV(key, 16, v, 16);

        SecByteBlock d(l-8-16);
        StreamTransformationFilter f(dec, new ArraySink(d, d.size()));
        inFile.CopyTo(f);
        f.MessageEnd();

        std::cout << "Key: ";
        StringSource(key, 16, true, new Redirector(encoder));
        std::cout << std::endl;

        std::cout << "IV: ";
        StringSource(v, 16, true, new Redirector(encoder));
        std::cout << std::endl;

        std::cout << "Data: ";
        StringSource(d, d.size(), true, new Redirector(encoder));
        std::cout << std::endl;

        message.assign(reinterpret_cast<const char*>(d.data()), d.size());
    }

    std::cout << "Message: ";
    std::cout << message << std::endl;

    return 0;
}

Запуск программы приводит к:

$ g++ test.cxx ./libcryptopp.a -o test.exe
$ ./test.exe
Key: 01020304010203040102030401020304
IV: 08070605080706050807060508070605
Data: 4E6F77206973207468652074696D6520666F7220616C6C20676F6F64206D656E20746F2063
6F6D6520746F207468652061696465206F6620746865697220636F756E7472792E

Key: 01020304010203040102030401020304
IV: 08070605080706050807060508070605
Data: 4E6F77206973207468652074696D6520666F7220616C6C20676F6F64206D656E20746F2063
6F6D6520746F207468652061696465206F6620746865697220636F756E7472792E

Message: Now is the time for all good men to come to the aide of their country.

ДоВ этом вопросе переполнения стека библиотека Crypto ++ не предоставила PutWord64 и GetWord64.Взаимодействие с библиотеками, такими как Python, важно для проекта, поэтому они были добавлены в Commit 6d69043403a9 и Commit 8260dd1e81c3 .Они будут частью выпуска Crypto ++ 8.3.

Если вы работаете с Crypto ++ 8.2 или ниже, вы можете выполнить 64-битное чтение с помощью следующего кода:

word64 length;
word32 h, l;

inFile.GetWord32(h, BIG_ENDIAN_ORDER);
inFile.GetWord32(l, BIG_ENDIAN_ORDER);

length = ((word64)h << 32) | l;

Вот файл данных, использованный для этого примера.

$ hexdump -C test.dat
00000000  00 00 00 00 00 00 00 5e  08 07 06 05 08 07 06 05  |.......^........|
00000010  08 07 06 05 08 07 06 05  b0 82 79 ee a6 d8 8a 0e  |..........y.....|
00000020  a6 b3 a4 7e 63 bd 9a bc  0e e4 b6 be 3e eb 36 64  |...~c.......>.6d|
00000030  72 cd ba 91 8d e0 d3 c5  cd 64 ae c0 51 de a7 c9  |r........d..Q...|
00000040  1e a8 81 6d c0 d5 42 2a  17 5a 19 62 1e 9c ab fd  |...m..B*.Z.b....|
00000050  21 3d b0 8f e2 b3 7a d4  08 8d ec 00 e0 1e 5e 78  |!=....z.......^x|
00000060  56 6d f5 3e 8c 5f fe 54                           |Vm.>._.T|
0 голосов
/ 09 июля 2019

Похоже, проблема была связана с заполнением.Вместо этого я переключился на использование StringSource, которое работало только после того, как я указал CryptoPP::BlockPaddingSchemeDef::BlockPaddingScheme::ZEROS_PADDING в качестве аргумента для StreamTransformationFilter

Вот рабочий код для всех, кто интересуется:

    void Crypto::decryptFileAES(CryptoPP::SecByteBlock key, std::string infilePath) {
        std::ifstream fin(infilePath, std::ifstream::binary);

        // Get the length of the file in bytes
        fin.seekg (0, fin.end);
        size_t fileLen = fin.tellg();
        fin.seekg (0, fin.beg);

        // First 8 bytes is the file size
        std::vector<char> fileSizeVec(8);
        fin.read(fileSizeVec.data(), fileSizeVec.size());

        // Read the first 16 bytes to get the initialization vector
        std::vector<char> ivBuffer(16);
        fin.read(ivBuffer.data(), ivBuffer.size());
        CryptoPP::SecByteBlock iv(reinterpret_cast<const unsigned char*>(ivBuffer.data()), ivBuffer.size());

        // Create a CBC decryptor
        CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption decryption;
        decryption.SetKeyWithIV(key, sizeof(key), iv);

        size_t bytesReamining = fileLen - fin.tellg();
        std::vector<char> buffer(bytesReamining);

        if(!fin.read(buffer.data(), bytesReamining)) {
            throw std::runtime_error("Unable to read file");
        }

        std::string decryptedText;

        CryptoPP::StringSource ss(reinterpret_cast<const unsigned char*>(buffer.data()), buffer.size(), true,
                new CryptoPP::StreamTransformationFilter(decryption,
                        new CryptoPP::StringSink(decryptedText), CryptoPP::BlockPaddingSchemeDef::BlockPaddingScheme::ZEROS_PADDING));

        std::cout << decryptedText << std::endl;
    }
...