Erstellen eines Hashs mit CNG

Ein Hash ist ein Einwegvorgang, der auf einem Datenblock ausgeführt wird, um einen eindeutigen Hashwert zu erstellen, der den Inhalt der Daten darstellt. Unabhängig davon, wann der Hash ausgeführt wird, erzeugt derselbe Hashingalgorithmus , der auf denselben Daten ausgeführt wird, immer denselben Hashwert. Wenn sich eine der Daten ändert, ändert sich der Hashwert entsprechend.

Hashes sind für die Verschlüsselung von Daten nicht nützlich, da sie nicht zum Reproduzieren der ursprünglichen Daten aus dem Hashwert verwendet werden sollen. Hashes sind am nützlichsten, um die Integrität der Daten zu überprüfen, wenn sie mit einem asymmetrischen Signaturalgorithmus verwendet werden. Wenn Sie z. B. eine Textnachricht signiert, den Hash signiert und den signierten Hashwert in die ursprüngliche Nachricht aufgenommen haben, könnte der Empfänger den signierten Hash überprüfen, den Hashwert für die empfangene Nachricht erstellen und diesen Hashwert dann mit dem signierten Hashwert vergleichen, der in der ursprünglichen Nachricht enthalten ist. Wenn die beiden Hashwerte identisch sind, kann der Empfänger vernünftigerweise sicherstellen, dass die ursprüngliche Nachricht nicht geändert wurde.

Die Größe des Hashwerts wird für einen bestimmten Hashingalgorithmus festgelegt. Dies bedeutet, dass unabhängig davon, wie groß oder klein der Datenblock ist, der Hashwert immer dieselbe Größe ist. Beispielsweise weist der SHA256-Hashingalgorithmus eine Hashwertgröße von 256 Bit auf.

Erstellen eines Hashing-Objekts

Führen Sie die folgenden Schritte aus, um einen Hash mit CNG zu erstellen:

  1. Öffnen Sie einen Algorithmusanbieter, der den gewünschten Algorithmus unterstützt. Typische Hashingalgorithmen umfassen MD2, MD4, MD5, SHA-1 und SHA256. Rufen Sie die BCryptOpenAlgorithmProvider-Funktion auf, und geben Sie den entsprechenden Algorithmusbezeichner im pszAlgId-Parameter an. Die Funktion gibt einen Handle an den Anbieter zurück.

  2. Führen Sie die folgenden Schritte aus, um das Hashingobjekt zu erstellen:

    1. Rufen Sie die Größe des Objekts ab, indem Sie die BCryptGetProperty-Funktion aufrufen, um die BCRYPT_OBJECT_LENGTH-Eigenschaft abzurufen.
    2. Zuweisen des Speichers zum Halten des Hashobjekts.
    3. Erstellen Sie das Objekt, indem Sie die BCryptCreateHash-Funktion aufrufen.
  3. Hashen Sie die Daten. Dies umfasst das Aufrufen der BCryptHashData-Funktion einmal oder mehrere Male. Jeder Aufruf fügt die angegebenen Daten an den Hash an.

  4. Führen Sie die folgenden Schritte aus, um den Hashwert abzurufen:

    1. Rufen Sie die Größe des Werts ab, indem Sie die BCryptGetProperty-Funktion aufrufen, um die BCRYPT_HASH_LENGTH-Eigenschaft abzurufen.
    2. Weisen Sie den Speicher zu, um den Wert zu halten.
    3. Rufen Sie den Hashwert ab, indem Sie die BCryptFinishHash-Funktion aufrufen. Nachdem diese Funktion aufgerufen wurde, ist das Hashobjekt nicht mehr gültig.
  5. Um dieses Verfahren abzuschließen, müssen Sie die folgenden Bereinigungsschritte ausführen:

    1. Schließen Sie das Hashobjekt, indem Sie das Hashhandle an die BCryptDestroyHash-Funktion übergeben.

    2. Geben Sie den Speicher frei, den Sie für das Hashobjekt zugewiesen haben.

    3. Wenn Sie keine weiteren Hashobjekte erstellen, schließen Sie den Algorithmusanbieter, indem Sie den Anbieterhandle an die BCryptCloseAlgorithmProvider-Funktion übergeben.

      Wenn Sie mehr Hashobjekte erstellen, empfehlen wir, den Algorithmusanbieter wiederzuverwenden, anstatt denselben Algorithmusanbieter mehrmals zu erstellen und zu zerstören.

    4. Wenn Sie die Verwendung des Hashwertspeichers abgeschlossen haben, geben Sie ihn frei.

Das folgende Beispiel zeigt, wie Sie einen Hashwert mithilfe von CNG erstellen.

// 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);
    }

}

Erstellen eines wiederverwendbaren Hashingobjekts

Ab Windows 8 und Windows Server 2012 können Sie ein wiederverwendbares Hashingobjekt für Szenarien erstellen, in denen Sie mehrere Hashes oder HMACs in schneller Folge berechnen müssen. Geben Sie dazu die BCRYPT_HASH_REUSABLE_FLAG an, wenn Sie die Funktion BCryptOpenAlgorithmProvider aufrufen. Alle Microsoft-Hashalgorithmusanbieter unterstützen dieses Kennzeichen. Ein mithilfe dieses Flags erstelltes Hashingobjekt kann unmittelbar nach dem Aufrufen von BCryptFinishHash wiederverwendet werden, genau so, als ob es neu erstellt wurde, indem BCryptCreateHash aufgerufen wurde. Führen Sie die folgenden Schritte aus, um ein wiederverwendbares Hashingobjekt zu erstellen:

  1. Öffnen Sie einen Algorithmusanbieter, der den gewünschten Hashingalgorithmus unterstützt. Rufen Sie die BCryptOpenAlgorithmProvider-Funktion auf, und geben Sie den entsprechenden Algorithmusbezeichner im PszAlgId-Parameter und BCRYPT_HASH_REUSABLE_FLAG im dwFlags-Parameter an. Die Funktion gibt einen Handle an den Anbieter zurück.

  2. Führen Sie die folgenden Schritte aus, um das Hashingobjekt zu erstellen:

    1. Rufen Sie die Größe des Objekts ab, indem Sie die BCryptGetProperty-Funktion aufrufen, um die BCRYPT_OBJECT_LENGTH-Eigenschaft abzurufen.
    2. Zuweisen des Speichers zum Halten des Hashobjekts.
    3. Erstellen Sie das Objekt, indem Sie die BCryptCreateHash-Funktion aufrufen. Geben Sie BCRYPT_HASH_REUSABLE_FLAG im dwFlags-Parameter an .
  3. Hashen Sie die Daten, indem Sie die BCryptHashData-Funktion aufrufen.

  4. Führen Sie die folgenden Schritte aus, um den Hashwert abzurufen:

    1. Rufen Sie die Größe des Hashwerts ab, indem Sie die BCryptGetProperty-Funktion aufrufen, um die BCRYPT_HASH_LENGTH Eigenschaft abzurufen.
    2. Weisen Sie den Speicher zu, um den Wert zu halten.
    3. Rufen Sie den Hashwert ab, indem Sie BCryptFinishHash aufrufen.
  5. Um das Hashingobjekt mit neuen Daten wiederzuverwenden, wechseln Sie zu Schritt 3.

  6. Um dieses Verfahren abzuschließen, müssen Sie die folgenden Bereinigungsschritte ausführen:

    1. Schließen Sie das Hashobjekt, indem Sie das Hashhandle an die BCryptDestroyHash-Funktion übergeben.
    2. Geben Sie den Speicher frei, den Sie für das Hashobjekt zugewiesen haben.
    3. Wenn Sie keine weiteren Hashobjekte erstellen, schließen Sie den Algorithmusanbieter, indem Sie den Anbieterhandle an die BCryptCloseAlgorithmProvider-Funktion übergeben.
    4. Wenn Sie die Verwendung des Hashwertspeichers abgeschlossen haben, geben Sie ihn frei.

Duplizieren eines Hashobjekts

Unter bestimmten Umständen kann es nützlich sein, einige allgemeine Daten zu hashen und dann zwei separate Hashobjekte aus den allgemeinen Daten zu erstellen. Sie müssen nicht zwei separate Hashobjekte erstellen und die allgemeinen Daten zweimal hashen, um dies zu erreichen. Sie können ein einzelnes Hashobjekt erstellen und alle allgemeinen Daten zum Hashobjekt hinzufügen. Anschließend können Sie die BCryptDuplicateHash-Funktion verwenden, um ein Duplikat des ursprünglichen Hashobjekts zu erstellen. Das duplizierte Hashobjekt enthält alle gleichen Statusinformationen und Hashdaten wie das Original, ist aber ein vollständig unabhängiges Hashobjekt. Sie können nun den einzelnen Hashobjekten die eindeutigen Daten hinzufügen und den Hashwert abrufen, wie im Beispiel gezeigt. Diese Technik ist nützlich, wenn sie eine möglicherweise große Menge gängiger Daten hashen. Sie müssen nur einmal die allgemeinen Daten zum ursprünglichen Hash hinzufügen, und dann können Sie das Hashobjekt duplizieren, um ein eindeutiges Hashobjekt abzurufen.