Правильно получить sha-1 для файлов, использующих openssl - PullRequest
3 голосов
/ 12 августа 2010

Я пытаюсь получить sha-1 для нескольких файлов.В настоящее время я делаю циклические файлы по заданному пути, открываю и читаю каждый файл отдельно, загружаю содержимое в буфер и затем отправляю его в функцию SHA openssl, чтобы получить хеш.Код выглядит примерно так:

    void ReadHashFile(LPCTSTR name)
{
 FILE * pFile;
 long lSize;
 char * buffer;
 size_t result;

 pFile = _tfopen ( name , L"rb" );
 if (pFile==NULL) {fputs ("File error",stderr); return;}

 // obtain file size:
 fseek (pFile , 0 , SEEK_END);
 lSize = ftell (pFile);
 rewind (pFile);

 if(lSize == -1){fputs ("Read Error",stderr);return;}

 // allocate memory to contain the whole file:
 buffer = (char*) malloc (sizeof(char)*lSize);
 if (buffer == NULL) {fputs ("Memory error",stderr); return;}

 // copy the file into the buffer:
 result = fread (buffer,1,lSize,pFile);
 if (result != lSize) {fputs ("Reading error",stderr); return;}

 /* the whole file is now loaded in the memory buffer. */

 // terminate
 fclose (pFile);

 //Do what ever with buffer
 unsigned char ibuf[] = "compute sha1";
 unsigned char obuf[20];

 SHA1((const unsigned char*)buffer, strlen((const char*)buffer), obuf);
 fwprintf(stderr, L"file %s\n", name);
 int i;
 for (i = 0; i < 20; i++) {
  printf("%02x ", obuf[i]);
 }
 printf("\n");


 free(buffer);
}

Некоторые файлы кажутся нечитаемыми, некоторые дают мне размер -1, другие, я могу прочитать только первые 2-3 байта, что дает много файлов одинакового шадаже если они разные.

Буду признателен, если кто-нибудь сможет мне помочь с этим или если у кого-то есть опыт хеширования файлов.Да, и есть ли способ получить файл sha1 без предварительной загрузки всего файла в память, я имею в виду, что для больших файлов это решение не будет работать.

С уважением

1 Ответ

16 голосов
/ 12 августа 2010

Если у вас возникли проблемы с чтением содержимого файла, до до вызова кода хеш-функции, тогда ваша проблема не связана с хешированием.

Вы должны использовать стандартную функцию fopen() вместо _tfopen(). В C вещей, которые начинаются с символа подчеркивания, часто лучше избегать. Тем более, что _tfopen(), по-видимому, соответствует либо fopen(), либо специфичному для Windows _wfopen() в зависимости от того, активирована ли так называемая "поддержка юникода". Кроме того, в чисто Windows-приложении вы можете использовать такие функции Win32, как CreateFile().

Чтение всего файла в памяти и последующее хеширование - это сырая. Например, он не сможет обрабатывать файлы, размер которых превышает объем доступной оперативной памяти. Кроме того, чтобы узнать размер файла, вы должны искать в нем, что ненадежно (могут быть псевдофайлы, которые на самом деле являются каналами в некоторый процесс генерации данных, поиск которого невозможен). Хеш-функции могут обрабатывать данные кусками; Вы должны использовать небольшой буфер (8 кБ это традиционный размер) и использовать функции SHA1_Init(), SHA1_Update() и SHA1_Final().

fread() не обязательно считывает столько данных, сколько вы запрашивали. И это не ошибка.

Когда вы звоните SHA1(), вы используете strlen() в своем буфере, который является поддельным. strlen() возвращает длину строки символов ; Проще говоря, число байтов до следующего байта значения ноль. Многие файлы содержат байты со значением 0. А если этого файла нет, то нет никакой гарантии, что в вашем буфере есть какой-либо байт со значением 0, так что вызов strlen() может закончить чтение памяти вне выделенного буфера (это плохо ). Поскольку вы столкнулись с проблемой получения длины файла и выделения такого большого буфера, вы должны по крайней мере использовать эту длину вместо попытки пересчитать ее с помощью функции, которая этого не делает.

Подводя итог: ваш код должен выглядеть так (не проверено):

/*
 * Hash a file, which name is given. Hash output is written out in
 * buffer "out[]". The hash output consists in exactly 20 bytes.
 * On success, 0 is returned; on error, returned value is -1 and
 * out[] is unaltered.
 */
int
do_sha1_file(char *name, unsigned char *out)
{
    FILE *f;
    unsigned char buf[8192];
    SHA_CTX sc;
    int err;

    f = fopen(name, "rb");
    if (f == NULL) {
        /* do something smart here: the file could not be opened */
        return -1;
    }
    SHA1_Init(&sc);
    for (;;) {
        size_t len;

        len = fread(buf, 1, sizeof buf, f);
        if (len == 0)
            break;
        SHA1_Update(&sc, buf, len);
    }
    err = ferror(f);
    fclose(f);
    if (err) {
        /* some I/O error was encountered; report the error */
        return -1;
    }
    SHA1_Final(out, &sc);
    return 0;
}

И не забудьте включить соответствующие заголовки файлов! (<stdio.h> и sha.h от OpenSSL)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...