CNG を使用したハッシュの作成

ハッシュは、データの内容を表す一意のハッシュ値を作成するためにデータ ブロックに対して実行される一方向の操作です。 ハッシュがいつ実行されても、同じデータに対して同じ ハッシュ アルゴリズム が実行されると、常に同じハッシュ値が生成されます。 いずれかのデータが変更された場合、ハッシュ値は適切に変更されます。

ハッシュは、ハッシュ値から元のデータを再現するために使用されることを意図していないため、データの暗号化には役立ちません。 ハッシュは、非対称署名アルゴリズムで使用する場合にデータの整合性を確認するのに最も役立ちます。 たとえば、テキスト メッセージをハッシュし、ハッシュに署名し、署名されたハッシュ値を元のメッセージと共に含めた場合、受信者は署名されたハッシュを確認し、受信したメッセージのハッシュ値を作成し、このハッシュ値を元のメッセージに含まれる署名付きハッシュ値と比較できます。 2 つのハッシュ値が同じ場合、受信者は元のメッセージが変更されていないことを合理的に確認できます。

ハッシュ値のサイズは、特定のハッシュ アルゴリズムに対して固定されます。 つまり、データ ブロックの大きさや小ささにかかわらず、ハッシュ値は常に同じサイズになります。 たとえば、SHA256 ハッシュ アルゴリズムのハッシュ値サイズは 256 ビットです。

ハッシュ オブジェクトの作成

CNG を使用してハッシュを作成するには、次の手順に従います。

  1. 目的のアルゴリズムをサポートするアルゴリズム プロバイダーを開きます。 一般的なハッシュ アルゴリズムには、MD2、MD4、MD5、SHA-1、SHA256 が含まれます。 BCryptOpenAlgorithmProvider 関数を呼び出し、pszAlgId パラメーターに適切なアルゴリズム識別子を指定します。 関数は、プロバイダーにハンドルを返します。

  2. ハッシュ オブジェクトを作成するには、次の手順を実行します。

    1. BCryptGetProperty 関数を呼び出してオブジェクトのサイズを取得し、BCRYPT_OBJECT_LENGTH プロパティを取得します。
    2. ハッシュ オブジェクトを保持するためにメモリを割り当てます。
    3. BCryptCreateHash 関数を呼び出してオブジェクトを作成します。
  3. データをハッシュします。 これには、 BCryptHashData 関数を 1 回以上呼び出す必要があります。 各呼び出しでは、指定したデータがハッシュに追加されます。

  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以降では、複数のハッシュまたは HMAC を連続して計算する必要があるシナリオで、再利用可能なハッシュ オブジェクトを作成できます。 これを行うには、BCryptOpenAlgorithmProvider 関数を呼び出すときにBCRYPT_HASH_REUSABLE_FLAGを指定します。 すべての Microsoft ハッシュ アルゴリズム プロバイダーがこのフラグをサポートしています。 このフラグを使用して作成されたハッシュ オブジェクトは、 BCryptCreateHash を呼び出して新しく作成されたかのように、 BCryptFinishHash を呼び出した直後に再利用できます。 再利用可能なハッシュ オブジェクトを作成するには、次の手順を実行します。

  1. 目的のハッシュ アルゴリズムをサポートするアルゴリズム プロバイダーを開きます。 BCryptOpenAlgorithmProvider 関数を呼び出し、pszAlgId パラメーターに適切なアルゴリズム識別子を指定し、dwFlags パラメーターにBCRYPT_HASH_REUSABLE_FLAGします。 関数は、プロバイダーにハンドルを返します。

  2. ハッシュ オブジェクトを作成するには、次の手順を実行します。

    1. BCryptGetProperty 関数を呼び出してオブジェクトのサイズを取得し、BCRYPT_OBJECT_LENGTH プロパティを取得します。
    2. ハッシュ オブジェクトを保持するためにメモリを割り当てます。
    3. BCryptCreateHash 関数を呼び出してオブジェクトを作成します。 dwFlags パラメーターでBCRYPT_HASH_REUSABLE_FLAGを指定します。
  3. BCryptHashData 関数を呼び出してデータをハッシュします。

  4. ハッシュ値を取得するには、次の手順を実行します。

    1. BCryptGetProperty 関数を呼び出してハッシュ値のサイズを取得し、BCRYPT_HASH_LENGTH プロパティを取得します。
    2. 値を保持するメモリを割り当てます。
    3. BCryptFinishHash を呼び出してハッシュ値を取得します。
  5. ハッシュ オブジェクトを新しいデータと共に再利用するには、手順 3 に進みます。

  6. この手順を完了するには、次のクリーンアップ手順を実行する必要があります。

    1. ハッシュ ハンドルを BCryptDestroyHash 関数に渡して、ハッシュ オブジェクトを閉じます。
    2. ハッシュ オブジェクトに割り当てたメモリを解放します。
    3. これ以上ハッシュ オブジェクトを作成しない場合は、プロバイダー ハンドルを BCryptCloseAlgorithmProvider 関数に渡してアルゴリズム プロバイダーを閉じます。
    4. ハッシュ値メモリの使用が完了したら、解放します。

ハッシュ オブジェクトの複製

状況によっては、ある程度の共通データをハッシュしてから、共通データから 2 つの個別のハッシュ オブジェクトを作成すると便利な場合があります。 これを実現するために、2 つの個別のハッシュ オブジェクトを作成し、共通データを 2 回ハッシュする必要はありません。 1 つのハッシュ オブジェクトを作成し、すべての共通データをハッシュ オブジェクトに追加できます。 次に、 BCryptDuplicateHash 関数を使用して、元のハッシュ オブジェクトの複製を作成できます。 重複するハッシュ オブジェクトには、元と同じ状態情報とハッシュされたデータがすべて含まれますが、完全に独立したハッシュ オブジェクトです。 各ハッシュ オブジェクトに一意のデータを追加し、例に示すようにハッシュ値を取得できるようになりました。 この手法は、大量の共通データをハッシュする場合に便利です。 共通データを元のハッシュに 1 回だけ追加するだけで、ハッシュ オブジェクトを複製して一意のハッシュ オブジェクトを取得できます。