Création d’un hachage avec CNG

Un hachage est une opération unidirectionnelle effectuée sur un bloc de données pour créer une valeur de hachage unique qui représente le contenu des données. Quel que soit le moment où le hachage est effectué, le même algorithme de hachage effectué sur les mêmes données génère toujours la même valeur de hachage. Si l’une des données change, la valeur de hachage est modifiée de manière appropriée.

Les hachages ne sont pas utiles pour chiffrer des données, car ils ne sont pas destinés à être utilisés pour reproduire les données d’origine à partir de la valeur de hachage. Les hachages sont les plus utiles pour vérifier l’intégrité des données lorsqu’ils sont utilisés avec un algorithme de signature asymétrique. Par exemple, si vous avez haché un message texte, signé le hachage et inclus la valeur de hachage signé avec le message d’origine, le destinataire peut vérifier le hachage signé, créer la valeur de hachage pour le message reçu, puis comparer cette valeur de hachage à la valeur de hachage signée incluse avec le message d’origine. Si les deux valeurs de hachage sont identiques, le destinataire peut être raisonnablement sûr que le message d’origine n’a pas été modifié.

La taille de la valeur de hachage est fixe pour un algorithme de hachage particulier. Cela signifie que, quelle que soit la taille ou la taille du bloc de données, la valeur de hachage sera toujours de la même taille. Par exemple, l’algorithme de hachage SHA256 a une taille de valeur de hachage de 256 bits.

Création d’un objet de hachage

Pour créer un hachage à l’aide de CNG, procédez comme suit :

  1. Ouvrez un fournisseur d’algorithmes qui prend en charge l’algorithme souhaité. Les algorithmes de hachage classiques incluent MD2, MD4, MD5, SHA-1 et SHA256. Appelez la fonction BCryptOpenAlgorithmProvider et spécifiez l’identificateur d’algorithme approprié dans le paramètre pszAlgId . La fonction retourne un handle au fournisseur.

  2. Effectuez les étapes suivantes pour créer l’objet de hachage :

    1. Obtenez la taille de l’objet en appelant la fonction BCryptGetProperty pour récupérer la propriété BCRYPT_OBJECT_LENGTH .
    2. Allouez de la mémoire pour contenir l’objet de hachage.
    3. Créez l’objet en appelant la fonction BCryptCreateHash .
  3. Hachage des données. Cela implique d’appeler la fonction BCryptHashData une ou plusieurs fois. Chaque appel ajoute les données spécifiées au hachage.

  4. Effectuez les étapes suivantes pour obtenir la valeur de hachage :

    1. Récupérez la taille de la valeur en appelant la fonction BCryptGetProperty pour obtenir la propriété BCRYPT_HASH_LENGTH .
    2. Allouez de la mémoire pour contenir la valeur.
    3. Récupérez la valeur de hachage en appelant la fonction BCryptFinishHash . Une fois cette fonction appelée, l’objet de hachage n’est plus valide.
  5. Pour effectuer cette procédure, vous devez effectuer les étapes de nettoyage suivantes :

    1. Fermez l’objet de hachage en passant la poignée de hachage à la fonction BCryptDestroyHash .

    2. Libérez la mémoire que vous avez allouée pour l’objet de hachage.

    3. Si vous ne créez plus d’objets de hachage, fermez le fournisseur d’algorithmes en passant le handle du fournisseur à la fonction BCryptCloseAlgorithmProvider .

      Si vous souhaitez créer plus d’objets de hachage, nous vous suggérons de réutiliser le fournisseur d’algorithmes plutôt que de créer et de détruire le même type de fournisseur d’algorithmes plusieurs fois.

    4. Lorsque vous avez terminé d’utiliser la mémoire de valeur de hachage, libérez-la.

L’exemple suivant montre comment créer une valeur de hachage à l’aide de 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);
    }

}

Création d’un objet de hachage réutilisable

À compter de Windows 8 et Windows Server 2012, vous pouvez créer un objet de hachage réutilisable pour les scénarios qui nécessitent de calculer plusieurs hachages ou HMAC en succession rapide. Pour ce faire, spécifiez le BCRYPT_HASH_REUSABLE_FLAG lors de l’appel de la fonction BCryptOpenAlgorithmProvider . Tous les fournisseurs d’algorithmes de hachage Microsoft prennent en charge cet indicateur. Un objet de hachage créé à l’aide de cet indicateur peut être réutilisé immédiatement après l’appel de BCryptFinishHash comme s’il avait été créé en appelant BCryptCreateHash. Effectuez les étapes suivantes pour créer un objet de hachage réutilisable :

  1. Ouvrez un fournisseur d’algorithmes qui prend en charge l’algorithme de hachage souhaité. Appelez la fonction BCryptOpenAlgorithmProvider et spécifiez l’identificateur d’algorithme approprié dans le paramètre pszAlgId et BCRYPT_HASH_REUSABLE_FLAG dans le paramètre dwFlags . La fonction retourne un handle au fournisseur.

  2. Effectuez les étapes suivantes pour créer l’objet de hachage :

    1. Obtenez la taille de l’objet en appelant la fonction BCryptGetProperty pour récupérer la propriété BCRYPT_OBJECT_LENGTH .
    2. Allouez de la mémoire pour contenir l’objet de hachage.
    3. Créez l’objet en appelant la fonction BCryptCreateHash . Spécifiez BCRYPT_HASH_REUSABLE_FLAG dans le paramètre dwFlags .
  3. Hachez les données en appelant la fonction BCryptHashData .

  4. Effectuez les étapes suivantes pour obtenir la valeur de hachage :

    1. Obtenez la taille de la valeur de hachage en appelant la fonction BCryptGetProperty pour obtenir la propriété BCRYPT_HASH_LENGTH .
    2. Allouez de la mémoire pour contenir la valeur.
    3. Obtenez la valeur de hachage en appelant BCryptFinishHash.
  5. Pour réutiliser l’objet de hachage avec de nouvelles données, passez à l’étape 3.

  6. Pour effectuer cette procédure, vous devez effectuer les étapes de nettoyage suivantes :

    1. Fermez l’objet de hachage en passant la poignée de hachage à la fonction BCryptDestroyHash .
    2. Libérez la mémoire que vous avez allouée pour l’objet de hachage.
    3. Si vous ne créez plus d’objets de hachage, fermez le fournisseur d’algorithmes en passant le handle du fournisseur à la fonction BCryptCloseAlgorithmProvider .
    4. Lorsque vous avez terminé d’utiliser la mémoire de valeur de hachage, libérez-la.

Duplication d’un objet de hachage

Dans certains cas, il peut être utile de hacher une certaine quantité de données courantes, puis de créer deux objets de hachage distincts à partir des données communes. Pour ce faire, vous n’avez pas besoin de créer deux objets de hachage distincts et de hacher les données communes deux fois. Vous pouvez créer un objet de hachage unique et ajouter toutes les données courantes à l’objet de hachage. Ensuite, vous pouvez utiliser la fonction BCryptDuplicateHash pour créer un doublon de l’objet de hachage d’origine. L’objet de hachage en double contient toutes les mêmes informations d’état et données de hachage que l’objet d’origine, mais il s’agit d’un objet de hachage complètement indépendant. Vous pouvez maintenant ajouter les données uniques à chacun des objets de hachage et obtenir la valeur de hachage comme indiqué dans l’exemple. Cette technique est utile lors du hachage d’une grande quantité de données courantes. Vous n’avez qu’à ajouter les données courantes au hachage d’origine une seule fois, puis vous pouvez dupliquer l’objet de hachage pour obtenir un objet de hachage unique.