Creazione di un hash con CNG
Un hash è un'operazione unidirezionale eseguita su un blocco di dati per creare un valore hash univoco che rappresenta il contenuto dei dati. Indipendentemente dal momento in cui viene eseguito l'hash, lo stesso algoritmo di hash eseguito sugli stessi dati produrrà sempre lo stesso valore hash. Se uno dei dati cambia, il valore hash cambierà in modo appropriato.
Gli hash non sono utili per crittografare i dati perché non devono essere usati per riprodurre i dati originali dal valore hash. Gli hash sono più utili per verificare l'integrità dei dati quando vengono usati con un algoritmo di firma asimmetrica. Ad esempio, se è stato eseguito l'hashing di un messaggio di testo, è stato firmato l'hash e incluso il valore hash firmato con il messaggio originale, il destinatario potrebbe verificare l'hash firmato, creare il valore hash per il messaggio ricevuto e quindi confrontare questo valore hash con il valore hash firmato incluso nel messaggio originale. Se i due valori hash sono identici, il destinatario può essere ragionevolmente sicuro che il messaggio originale non sia stato modificato.
La dimensione del valore hash è fissa per un algoritmo hash specifico. Ciò significa che, indipendentemente dalle dimensioni del blocco di dati, il valore hash sarà sempre della stessa dimensione. Ad esempio, l'algoritmo hash SHA256 ha una dimensione del valore hash di 256 bit.
- Creazione di un oggetto hashing
- Creazione di un oggetto hash riutilizzabile
- duplicare un oggetto hash
Creazione di un oggetto hash
Per creare un hash con CNG, seguire questa procedura:
Aprire un provider di algoritmi che supporti l'algoritmo desiderato. Gli algoritmi hash tipici includono MD2, MD4, MD5, SHA-1 e SHA256. Chiamare la funzioneBCryptOpenAlgorithmProvidere specificare l'identificatore dell'algoritmo appropriato nel parametro pszAlgId. La funzione restituisce un handle al provider.
Per creare l'oggetto hash, seguire questa procedura:
Hash dei dati. Ciò comporta la chiamata alla funzioneBCryptHashData una o più volte. Ogni chiamata aggiunge i dati specificati all'hash.
Per ottenere il valore hash, seguire questa procedura:
Per completare questa procedura, è necessario eseguire la procedura di pulizia seguente:
Chiudere l'oggetto hash passando l'handle dell'hash alla funzione BCryptDestroyHash.
Liberare la memoria allocata per l'oggetto hash.
Se non si creeranno altri oggetti hash, chiudere il provider di algoritmi passando l'handle del provider alla funzione BCryptCloseAlgorithmProvider.
Se si creano più oggetti hash, è consigliabile riutilizzare il provider di algoritmi anziché creare ed eliminare più volte lo stesso tipo di provider di algoritmi.
Al termine dell'uso della memoria del valore hash, liberatela.
Nell'esempio seguente viene illustrato come creare un valore hash usando 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);
}
}
Creazione di un oggetto hash riutilizzabile
A partire da Windows 8 e Windows Server 2012, è possibile creare un oggetto hash riutilizzabile per scenari che richiedono di calcolare più hash o HMAC in rapida successione. A tale scopo, specificare il BCRYPT_HASH_REUSABLE_FLAG quando si chiama la funzione BCryptOpenAlgorithmProvider. Tutti i provider di algoritmi hash Microsoft supportano questo flag. Un oggetto hash creato usando questo flag può essere riutilizzato immediatamente dopo aver chiamato BCryptFinishHash come se fosse stato appena creato chiamando BCryptCreateHash. Per creare un oggetto hash riutilizzabile, seguire questa procedura:
Aprire un provider di algoritmi che supporti l'algoritmo hash desiderato. Chiamare la funzione diBCryptOpenAlgorithmProvidere specificare l'identificatore dell'algoritmo appropriato nel parametro pszAlgId e BCRYPT_HASH_REUSABLE_FLAG nel parametro dwFlags. La funzione restituisce un handle al provider.
Per creare l'oggetto hash, seguire questa procedura:
Eseguire l'hashing dei dati chiamando la funzione BCryptHashData.
Per ottenere il valore hash, seguire questa procedura:
- Ottenere la dimensione del valore hash chiamando la funzione BCryptGetProperty per ottenere la lunghezza della proprietà BCRYPT_HASH_LENGTH.
- Allocare memoria per contenere il valore.
- Ottenere il valore hash chiamando BCryptFinishHash.
Per riutilizzare l'oggetto hashing con nuovi dati, andare al passaggio 3.
Per completare questa procedura, è necessario eseguire la procedura di pulizia seguente:
- Chiudere l'oggetto hash passando l'handle dell'hash alla funzione BCryptDestroyHash.
- Liberare la memoria allocata per l'oggetto hash.
- Se non si creeranno altri oggetti hash, chiudere il provider di algoritmi passando l'handle del provider alla funzione BCryptCloseAlgorithmProvider.
- Al termine dell'uso della memoria del valore hash, liberarla.
Duplicazione di un oggetto hash
In alcune circostanze, può essere utile eseguire l'hashing di alcune quantità di dati comuni e quindi creare due oggetti hash separati dai dati comuni. Non è necessario creare due oggetti hash separati ed eseguire l'hashing dei dati comuni due volte per eseguire questa operazione. È possibile creare un singolo oggetto hash e aggiungere tutti i dati comuni all'oggetto hash. È quindi possibile usare la funzioneBCryptDuplicateHashper creare un duplicato dell'oggetto hash originale. L'oggetto hash duplicato contiene tutte le stesse informazioni sullo stato e i dati con hash dell'originale, ma è un oggetto hash completamente indipendente. È ora possibile aggiungere i dati univoci a ognuno degli oggetti hash e ottenere il valore hash, come illustrato nell'esempio. Questa tecnica è utile quando si esegue l'hashing di una grande quantità di dati comuni. È necessario aggiungere solo i dati comuni all'hash originale una volta e quindi è possibile duplicare l'oggetto hash per ottenere un oggetto hash univoco.