Я использую Microsoft CNG Cryptography API для вычисления хэша файла, пока все работает нормально, за исключением того, что вычисленный хэш неверен по сравнению с хешем, вычисленным с помощью внешней сторонней программы.
Я не уверен на 100%, но думаю, проблема в том, что я читаю файл в массив signed char
, а не в массив BYTE
(беззнаковый символ).
Ниже приведен код, как я сейчас читаю файл: (Примечание: я пропустил код проверки ошибок)
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
?
Может быть, есть что-то еще, вызывающее плохой вычисленный хэш, я не знаю, скажите мне об этом.
Обновлено: Ниже приведен полностью рабочий код для вычисления хэша 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;
}
При вызове read
или write
это одна из немногих ситуаций, когда можно использовать reinterpret_cast
. В некоторых случаях нет другого жизнеспособного решения.
Итак, для вашего случая:
file.read(reinterpret_cast<char*>(data), file_length);
@zebanovich Возможно, проблема не в чтении? Может проблема в данных? Возможно, проблема в has-вычислении? Пожалуйста, задайте об этом вопрос новый.
Спасибо! Ваше решение работает, и я обнаружил, что проблема с моим кодом заключалась в том, что я хэшировал только первый BYTE вместо записи file_lenght :)
Вычисленный хэш по-прежнему неверен, так же, как если бы я читал как char. Знаете ли вы, почему вычисленный хэш неверен?