Creación de un hash con CNG

Un hash es una operación unidireccional que se realiza en un bloque de datos para crear un valor hash único que representa el contenido de los datos. Independientemente de cuándo se realice el hash, el mismo algoritmo hash realizado en los mismos datos siempre generará el mismo valor hash. Si alguno de los datos cambia, el valor hash cambiará correctamente.

Los hashes no son útiles para cifrar datos porque no están diseñados para reproducir los datos originales del valor hash. Los hashes son más útiles para comprobar la integridad de los datos cuando se usan con un algoritmo de firma asimétrica. Por ejemplo, si ha hashizado un mensaje de texto, firmó el hash e incluyó el valor hash firmado con el mensaje original, el destinatario podría comprobar el hash firmado, crear el valor hash para el mensaje recibido y, a continuación, comparar este valor hash con el valor hash firmado incluido con el mensaje original. Si los dos valores hash son idénticos, el destinatario puede estar razonablemente seguro de que el mensaje original no se ha modificado.

El tamaño del valor hash se fija para un algoritmo hash determinado. Esto significa que, independientemente del tamaño o el tamaño del bloque de datos pequeño, el valor hash siempre será el mismo tamaño. Por ejemplo, el algoritmo hash SHA256 tiene un tamaño de valor hash de 256 bits.

Creación de un objeto hash

Para crear un hash mediante CNG, realice los pasos siguientes:

  1. Abra un proveedor de algoritmos que admita el algoritmo deseado. Los algoritmos hash típicos incluyen MD2, MD4, MD5, SHA-1 y SHA256. Llame a la función BCryptOpenAlgorithmProvider y especifique el identificador de algoritmo adecuado en el parámetro pszAlgId . La función devuelve un identificador al proveedor.

  2. Realice los pasos siguientes para crear el objeto hash:

    1. Obtenga el tamaño del objeto llamando a la función BCryptGetProperty para recuperar la propiedad BCRYPT_OBJECT_LENGTH .
    2. Asigne memoria para contener el objeto hash.
    3. Cree el objeto llamando a la función BCryptCreateHash .
  3. Aplica un algoritmo hash a los datos. Esto implica llamar a la función BCryptHashData una o varias veces. Cada llamada anexa los datos especificados al hash.

  4. Realice los pasos siguientes para obtener el valor hash:

    1. Recupere el tamaño del valor mediante una llamada a la función BCryptGetProperty para obtener la propiedad BCRYPT_HASH_LENGTH .
    2. Asigne memoria para contener el valor.
    3. Recupere el valor hash llamando a la función BCryptFinishHash . Después de llamar a esta función, el objeto hash ya no es válido.
  5. Para completar este procedimiento, debe realizar los siguientes pasos de limpieza:

    1. Cierre el objeto hash pasando el identificador hash a la función BCryptDestroyHash .

    2. Libere la memoria asignada para el objeto hash.

    3. Si no va a crear más objetos hash, cierre el proveedor de algoritmos pasando el identificador del proveedor a la función BCryptCloseAlgorithmProvider .

      Si va a crear más objetos hash, se recomienda reutilizar el proveedor de algoritmos en lugar de crear y destruir el mismo tipo de proveedor de algoritmos muchas veces.

    4. Cuando haya terminado de usar la memoria de valor hash, libere la memoria.

En el ejemplo siguiente se muestra cómo crear un valor hash mediante 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);
    }

}

Creación de un objeto hash reutilizable

A partir de Windows 8 y Windows Server 2012, puede crear un objeto hash reutilizable para escenarios que requieren que calcule varios hash o HMAC en una sucesión rápida. Para ello, especifique el BCRYPT_HASH_REUSABLE_FLAG al llamar a la función BCryptOpenAlgorithmProvider . Todos los proveedores de algoritmos hash de Microsoft admiten esta marca. Un objeto hash creado con esta marca se puede reutilizar inmediatamente después de llamar a BCryptFinishHash como si se hubiera creado recién llamando a BCryptCreateHash. Realice los pasos siguientes para crear un objeto hash reutilizable:

  1. Abra un proveedor de algoritmos que admita el algoritmo hash deseado. Llame a la función BCryptOpenAlgorithmProvider y especifique el identificador de algoritmo adecuado en el parámetro pszAlgId y BCRYPT_HASH_REUSABLE_FLAG en el parámetro dwFlags . La función devuelve un identificador al proveedor.

  2. Realice los pasos siguientes para crear el objeto hash:

    1. Obtenga el tamaño del objeto llamando a la función BCryptGetProperty para recuperar la propiedad BCRYPT_OBJECT_LENGTH .
    2. Asigne memoria para contener el objeto hash.
    3. Cree el objeto llamando a la función BCryptCreateHash . Especifique BCRYPT_HASH_REUSABLE_FLAG en el parámetro dwFlags .
  3. Para aplicar un algoritmo hash a los datos, llame a la función BCryptHashData .

  4. Realice los pasos siguientes para obtener el valor hash:

    1. Obtenga el tamaño del valor hash llamando a la función BCryptGetProperty para obtener la propiedad BCRYPT_HASH_LENGTH .
    2. Asigne memoria para contener el valor.
    3. Obtenga el valor hash llamando a BCryptFinishHash.
  5. Para reutilizar el objeto hash con nuevos datos, vaya al paso 3.

  6. Para completar este procedimiento, debe realizar los siguientes pasos de limpieza:

    1. Cierre el objeto hash pasando el identificador hash a la función BCryptDestroyHash .
    2. Libere la memoria asignada para el objeto hash.
    3. Si no va a crear más objetos hash, cierre el proveedor de algoritmos pasando el identificador del proveedor a la función BCryptCloseAlgorithmProvider .
    4. Cuando haya terminado de usar la memoria de valor hash, libere la memoria.

Duplicar un objeto hash

En algunas circunstancias, puede ser útil aplicar un algoritmo hash a cierta cantidad de datos comunes y, a continuación, crear dos objetos hash independientes de los datos comunes. No es necesario crear dos objetos hash independientes y aplicar un hash a los datos comunes dos veces para lograrlo. Puede crear un único objeto hash y agregar todos los datos comunes al objeto hash. A continuación, puede usar la función BCryptDuplicateHash para crear un duplicado del objeto hash original. El objeto hash duplicado contiene toda la misma información de estado y datos hash que el original, pero es un objeto hash completamente independiente. Ahora puede agregar los datos únicos a cada uno de los objetos hash y obtener el valor hash como se muestra en el ejemplo. Esta técnica es útil al aplicar un algoritmo hash a una cantidad posiblemente grande de datos comunes. Solo tiene que agregar los datos comunes al hash original una vez y, a continuación, puede duplicar el objeto hash para obtener un objeto hash único.