std :: ifstream читает файл как BYTE (беззнаковый символ), а не как символ - PullRequest
1 голос
/ 08 апреля 2019

Я использую API шифрования CNG от Microsoft для вычисления хэша файла, но пока все работает нормально, за исключением того, что вычисленный хэш неверен по сравнению с хешем, вычисленным внешней сторонней программой.

Я не уверен на 100%, но думаю, что проблема в том, что я читаю файл в массив signed char вместо массива BYTE (unsigned char).

Ниже приведен код, по которому я читаю файл: ( примечание: Я пропустил код проверки ошибок)

std::ifstream file;
file.open("sample.txt", std::ifstream::binary | std::ifstream::in | std::ifstream::ate);
const int file_length = file.tellg();

char* data = new char[file_length];
file.seekg(file.beg);
file.read(data, file_length);

Проблема с приведенным выше кодом заключается в том, что данные считываются в массив знаков со знаком, но криптографические функции ожидают unsigned char / BYTE, и я предполагаю, что именно поэтому мой вычисленный хэш неверен.

Сильфонный код - это то, что я хотел бы сделать, но он не работает (добавлен комментарий), поскольку метод ifstream::read() ожидает char массив, а не unsigned char / BYTE

std::ifstream file;
file.open("sample.txt", std::ifstream::binary | std::ifstream::in | std::ifstream::ate);
const int file_length = file.tellg();

PBYTE data = new BYTE[file_length];
file.seekg(file.beg);
file.read(data, file_length); // error PBYTE is incompatibe with char*

Итак, мой вопрос: как мне читать данные как BYTE, а не char мне нужно использовать CreateFile API или есть способ с std::ifstream?

Может быть, что-то еще вызывает плохой вычисляемый хеш, я не знаю, скажи мне так.

EDIT: Ниже приведен полностью рабочий код для вычисления хэша SHA256 для заданного имени файла, но хеш плохой. (то есть не так, как если бы это было сделано с хэш-утилитой третьей стороны)

#include <windows.h>
#include <bcrypt.h>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <fstream>


#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)

std::string ByteToHex(PBYTE data, size_t len)
{
    std::stringstream ss;
    ss << std::hex << std::setfill('0') << std::uppercase;

    for (size_t i = 0; i < len; ++i)
    {
        ss << std::setw(2) << static_cast<short>(data[i]);
    }

    return ss.str();
}

int main()
{
    BCRYPT_ALG_HANDLE hAlg = nullptr;
    BCRYPT_HASH_HANDLE hHash = nullptr;
    DWORD cbHash, cbObject, cbData;
    PBYTE pbHash, pbObject;
    NTSTATUS status = STATUS_UNSUCCESSFUL;

    std::ifstream file;
    file.open("sample.exe", std::ifstream::binary | std::ifstream::in | std::ifstream::ate);
    const int file_length = file.tellg();

    if (!file.is_open())
    {
        abort();
    }

    PBYTE data = new BYTE[file_length];
    file.seekg(file.beg);
    file.read(reinterpret_cast<char*>(data), file_length);

    status = BCryptOpenAlgorithmProvider(
        &hAlg,
        BCRYPT_SHA256_ALGORITHM,
        nullptr,
        0);

    if (NT_SUCCESS(status))
    {
        status = BCryptGetProperty(
            hAlg,
            BCRYPT_OBJECT_LENGTH,
            reinterpret_cast<PBYTE>(&cbObject),
            sizeof(DWORD),
            &cbData,
            0);
    }
    else
    {
        abort();
    }

    pbObject = reinterpret_cast<PBYTE>(
        HeapAlloc(GetProcessHeap(), 0, cbObject));

    if (!pbObject)
    {
        abort();
    }

    if (NT_SUCCESS(status))
    {
        status = BCryptGetProperty(
            hAlg,
            BCRYPT_HASH_LENGTH,
            reinterpret_cast<PBYTE>(&cbHash),
            sizeof(DWORD),
            &cbData,
            0);
    }
    else
    {
        abort();
    }

    pbHash = reinterpret_cast<PBYTE>(
        HeapAlloc(GetProcessHeap(), 0, cbHash));

    if (!pbHash)
    {
        abort();
    }

    if (NT_SUCCESS(status))
    {
        status = BCryptCreateHash(
            hAlg,
            &hHash,
            pbObject,
            cbObject,
            nullptr,
            0,
            0);
    }
    else
    {
        abort();
    }

    if (NT_SUCCESS(status))
    {
        status = BCryptHashData(
            hHash,
            (PBYTE)(data),
            sizeof(data),
            0);
    }
    else
    {
        abort();
    }

    if (NT_SUCCESS(status))
    {
        status = BCryptFinishHash(
            hHash,
            pbHash,
            cbHash,
            0);
    }
    else
    {
        abort();
    }

    std::cout << ByteToHex(pbHash, cbHash).c_str();
    delete[] data;

    if(hAlg) BCryptCloseAlgorithmProvider(hAlg, 0);
    if(hHash) BCryptDestroyHash(hHash);
    if(pbHash) HeapFree(GetProcessHeap(), 0, pbHash);
    if(pbObject) HeapFree(GetProcessHeap(), 0, pbObject);
    return 0;
}

1 Ответ

2 голосов
/ 08 апреля 2019

При вызове read или write это одна из немногих ситуаций, когда считается правильным использовать reinterpret_cast.В некоторых случаях нет другого жизнеспособного решения.

Так что для вашего случая:

file.read(reinterpret_cast<char*>(data), file_length);
...