Поделиться через


Создание хэша с помощью 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. Закройте хэш-объект, передав хэш-дескриптор в функцию BCryptDestroyHash .

    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, можно создать повторно используемый объект хэширования для сценариев, требующих вычисления нескольких хэшей или HMACs в быстром последовательном порядке. Это можно сделать, указав 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. Закройте объект хэша, передав дескриптор хэша в функцию BCryptDestroyHash.
    2. Освободит память, выделенную для хэш-объекта.
    3. Если вы не будете создавать более хэш-объекты, закройте поставщик алгоритма, передав дескри птор поставщика в функцию BCryptCloseAlgorithmProvider.
    4. Когда закончишь использование памяти для хэш-значения, освободи её.

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

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