Создание хэша с помощью CNG

Хэш — это одностороння операция, которая выполняется с блоком данных для создания уникального хэш-значения, представляющего содержимое данных. Независимо от того, когда выполняется хэш, один и тот же алгоритм хэширования , выполняемый для одних и того же данных, всегда будет выдавать одно и то же хэш-значение. Если какие-либо данные изменяются, хэш-значение изменится соответствующим образом.

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

Размер хэш-значения является фиксированным для определенного алгоритма хэширования. Это означает, что независимо от того, насколько велик или мал блок данных, хэш-значение всегда будет иметь одинаковый размер. Например, алгоритм хэширования SHA256 имеет размер хэш-значения 256 бит.

Создание объекта хэширования

Чтобы создать хэш с помощью CNG, выполните следующие действия.

  1. Откройте поставщик алгоритмов, поддерживающий нужный алгоритм. К типичным алгоритмам хэширования относятся MD2, MD4, MD5, SHA-1 и SHA256. Вызовите функцию BCryptOpenAlgorithmProvider и укажите соответствующий идентификатор алгоритма в параметре pszAlgId . Функция возвращает дескриптор поставщику.

  2. Чтобы создать объект хэширования, выполните следующие действия:

    1. Получите размер объекта, вызвав функцию BCryptGetProperty для получения свойства BCRYPT_OBJECT_LENGTH .
    2. Выделение памяти для хранения хэш-объекта.
    3. Создайте объект , вызвав функцию BCryptCreateHash .
  3. Хэширования данных. Для этого необходимо вызвать функцию BCryptHashData один или несколько раз. Каждый вызов добавляет указанные данные в хэш.

  4. Чтобы получить хэш-значение, выполните следующие действия.

    1. Получите размер значения, вызвав функцию BCryptGetProperty , чтобы получить свойство BCRYPT_HASH_LENGTH .
    2. Выделение памяти для хранения значения.
    3. Получите хэш-значение, вызвав функцию BCryptFinishHash . После вызова этой функции объект хэша становится недействительным.
  5. Чтобы выполнить эту процедуру, необходимо выполнить следующие действия по очистке:

    1. Закройте хэш-объект, передав дескриптор хэша в функцию BCryptDeographyHash .

    2. Освободите память, выделенную для хэш-объекта.

    3. Если вы больше не будете создавать хэш-объекты, закройте поставщик алгоритма, передав дескриптор поставщика в функцию BCryptCloseAlgorithmProvider .

      Если вы будете создавать больше хэш-объектов, мы рекомендуем повторно использовать поставщик алгоритмов, а не создавать и уничтожать один и тот же тип поставщика алгоритмов много раз.

    4. Завершив использование памяти хэш-значения, освободите ее.

В следующем примере показано, как создать хэш-значение с помощью CNG.

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (C) Microsoft. All rights reserved.
/*++

Abstract:

    Sample program for SHA 256 hashing using CNG

--*/


#include <windows.h>
#include <stdio.h>
#include <bcrypt.h>



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

#define STATUS_UNSUCCESSFUL         ((NTSTATUS)0xC0000001L)


static const BYTE rgbMsg[] = 
{
    0x61, 0x62, 0x63
};


void __cdecl wmain(
                   int                      argc, 
                   __in_ecount(argc) LPWSTR *wargv)
{

    BCRYPT_ALG_HANDLE       hAlg            = NULL;
    BCRYPT_HASH_HANDLE      hHash           = NULL;
    NTSTATUS                status          = STATUS_UNSUCCESSFUL;
    DWORD                   cbData          = 0,
                            cbHash          = 0,
                            cbHashObject    = 0;
    PBYTE                   pbHashObject    = NULL;
    PBYTE                   pbHash          = NULL;

    UNREFERENCED_PARAMETER(argc);
    UNREFERENCED_PARAMETER(wargv);

    //open an algorithm handle
    if(!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(
                                                &hAlg,
                                                BCRYPT_SHA256_ALGORITHM,
                                                NULL,
                                                0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptOpenAlgorithmProvider\n", status);
        goto Cleanup;
    }

    //calculate the size of the buffer to hold the hash object
    if(!NT_SUCCESS(status = BCryptGetProperty(
                                        hAlg, 
                                        BCRYPT_OBJECT_LENGTH, 
                                        (PBYTE)&cbHashObject, 
                                        sizeof(DWORD), 
                                        &cbData, 
                                        0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptGetProperty\n", status);
        goto Cleanup;
    }

    //allocate the hash object on the heap
    pbHashObject = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbHashObject);
    if(NULL == pbHashObject)
    {
        wprintf(L"**** memory allocation failed\n");
        goto Cleanup;
    }

   //calculate the length of the hash
    if(!NT_SUCCESS(status = BCryptGetProperty(
                                        hAlg, 
                                        BCRYPT_HASH_LENGTH, 
                                        (PBYTE)&cbHash, 
                                        sizeof(DWORD), 
                                        &cbData, 
                                        0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptGetProperty\n", status);
        goto Cleanup;
    }

    //allocate the hash buffer on the heap
    pbHash = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbHash);
    if(NULL == pbHash)
    {
        wprintf(L"**** memory allocation failed\n");
        goto Cleanup;
    }

    //create a hash
    if(!NT_SUCCESS(status = BCryptCreateHash(
                                        hAlg, 
                                        &hHash, 
                                        pbHashObject, 
                                        cbHashObject, 
                                        NULL, 
                                        0, 
                                        0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptCreateHash\n", status);
        goto Cleanup;
    }
    

    //hash some data
    if(!NT_SUCCESS(status = BCryptHashData(
                                        hHash,
                                        (PBYTE)rgbMsg,
                                        sizeof(rgbMsg),
                                        0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptHashData\n", status);
        goto Cleanup;
    }
    
    //close the hash
    if(!NT_SUCCESS(status = BCryptFinishHash(
                                        hHash, 
                                        pbHash, 
                                        cbHash, 
                                        0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptFinishHash\n", status);
        goto Cleanup;
    }

    wprintf(L"Success!\n");

Cleanup:

    if(hAlg)
    {
        BCryptCloseAlgorithmProvider(hAlg,0);
    }

    if (hHash)    
    {
        BCryptDestroyHash(hHash);
    }

    if(pbHashObject)
    {
        HeapFree(GetProcessHeap(), 0, pbHashObject);
    }

    if(pbHash)
    {
        HeapFree(GetProcessHeap(), 0, pbHash);
    }

}

Создание повторно используемых объектов хэширования

Начиная с Windows 8 и Windows Server 2012, можно создать повторно используемый объект хэширования для сценариев, требующих быстрого вычисления нескольких хэшей или HMAC. Для этого укажите BCRYPT_HASH_REUSABLE_FLAG при вызове функции BCryptOpenAlgorithmProvider . Все поставщики хэш-алгоритмов Майкрософт поддерживают этот флаг. Объект хэширования, созданный с помощью этого флага, можно повторно использовать сразу после вызова BCryptFinishHash так же, как если бы он был недавно создан путем вызова BCryptCreateHash. Чтобы создать повторно используемый объект хэширования, выполните следующие действия.

  1. Откройте поставщик алгоритмов, поддерживающий нужный алгоритм хэширования. Вызовите функцию BCryptOpenAlgorithmProvider и укажите соответствующий идентификатор алгоритма в параметре pszAlgId и BCRYPT_HASH_REUSABLE_FLAG в параметре dwFlags . Функция возвращает дескриптор поставщику.

  2. Чтобы создать объект хэширования, выполните следующие действия:

    1. Получите размер объекта, вызвав функцию BCryptGetProperty для получения свойства BCRYPT_OBJECT_LENGTH .
    2. Выделение памяти для хранения хэш-объекта.
    3. Создайте объект , вызвав функцию BCryptCreateHash . Укажите BCRYPT_HASH_REUSABLE_FLAG в параметре dwFlags .
  3. Хэширования данных путем вызова функции BCryptHashData .

  4. Чтобы получить хэш-значение, выполните следующие действия.

    1. Получите размер хэш-значения, вызвав функцию BCryptGetProperty , чтобы получить свойство BCRYPT_HASH_LENGTH .
    2. Выделение памяти для хранения значения.
    3. Получите хэш-значение, вызвав BCryptFinishHash.
  5. Чтобы повторно использовать объект хэширования с новыми данными, перейдите к шагу 3.

  6. Чтобы выполнить эту процедуру, необходимо выполнить следующие действия по очистке:

    1. Закройте хэш-объект, передав дескриптор хэша в функцию BCryptDeographyHash .
    2. Освободите память, выделенную для хэш-объекта.
    3. Если вы больше не будете создавать хэш-объекты, закройте поставщик алгоритма, передав дескриптор поставщика в функцию BCryptCloseAlgorithmProvider .
    4. Завершив использование памяти хэш-значения, освободите ее.

Дублирование хэш-объекта

В некоторых случаях может быть полезно хэшировать некоторый объем общих данных, а затем создать два отдельных хэш-объекта из общих данных. Для этого не нужно создавать два отдельных хэш-объекта и хэшировать общие данные дважды. Вы можете создать один хэш-объект и добавить в него все общие данные. Затем можно использовать функцию BCryptDuplicateHash для создания дубликата исходного хэш-объекта. Повторяющийся хэш-объект содержит все те же сведения о состоянии и хэшированные данные, что и исходный, но является полностью независимым хэш-объектом. Теперь вы можете добавить уникальные данные в каждый из хэш-объектов и получить хэш-значение, как показано в примере. Этот метод полезен при хэширования возможно большого объема общих данных. Необходимо добавить общие данные в исходный хэш только один раз, а затем можно дублировать хэш-объект, чтобы получить уникальный хэш-объект.