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 dall'esecuzione dell'hash, lo stesso algoritmo 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'hash, 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.
Le dimensioni del valore hash sono fisse per un particolare algoritmo di hashing. Ciò significa che, indipendentemente dal fatto che il blocco di dati sia grande o piccolo, il valore hash sarà sempre la stessa dimensione. Ad esempio, l'algoritmo di hash SHA256 ha una dimensione del valore hash di 256 bit.
- Creazione di un oggetto hashing
- Creazione di un oggetto hashing riutilizzabile
- Duplicare un oggetto hash
Creazione di un oggetto hashing
Per creare un hash usando CNG, seguire questa procedura:
Aprire un provider di algoritmi che supporta l'algoritmo desiderato. Gli algoritmi di hash tipici includono MD2, MD4, MD5, SHA-1 e SHA256. Chiamare la funzione BCryptOpenAlgorithmProvider e specificare l'identificatore dell'algoritmo appropriato nel parametro pszAlgId . La funzione restituisce un handle al provider.
Seguire questa procedura per creare l'oggetto hashing:
- Ottenere le dimensioni dell'oggetto chiamando la funzione BCryptGetProperty per recuperare la proprietà BCRYPT_OBJECT_LENGTH .
- Allocare memoria per contenere l'oggetto hash.
- Creare l'oggetto chiamando la funzione BCryptCreateHash .
Hash dei dati. Ciò comporta la chiamata alla funzione BCryptHashData una o più volte. Ogni chiamata aggiunge i dati specificati all'hash.
Seguire questa procedura per ottenere il valore hash:
- Recuperare le dimensioni del valore chiamando la funzione BCryptGetProperty per ottenere la proprietà BCRYPT_HASH_LENGTH .
- Allocare memoria per contenere il valore.
- Recuperare il valore hash chiamando la funzione BCryptFinishHash . Dopo aver chiamato questa funzione, l'oggetto hash non è più valido.
Per completare questa procedura, è necessario eseguire la procedura di pulizia seguente:
Chiudere l'oggetto hash passando l'handle 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 creeranno più oggetti hash, è consigliabile riutilizzare il provider di algoritmi anziché creare e distruggere lo stesso tipo di provider di algoritmi molte volte.
Al termine dell'uso della memoria del valore hash, liberarla.
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 hashing riutilizzabile
A partire da Windows 8 e Windows Server 2012, è possibile creare un oggetto hashing riutilizzabile per scenari che richiedono di calcolare più hash o HMACs in successione rapida. Eseguire questa operazione specificando 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 proprio come se fosse stato appena creato chiamando BCryptCreateHash. Seguire questa procedura per creare un oggetto hashing riutilizzabile:
Aprire un provider di algoritmi che supporta l'algoritmo di hashing desiderato. Chiamare la funzione BCryptOpenAlgorithmProvider e specificare l'identificatore dell'algoritmo appropriato nel parametro pszAlgId e BCRYPT_HASH_REUSABLE_FLAG nel parametro dwFlags . La funzione restituisce un handle al provider.
Seguire questa procedura per creare l'oggetto hashing:
- Ottenere le dimensioni dell'oggetto chiamando la funzione BCryptGetProperty per recuperare la proprietà BCRYPT_OBJECT_LENGTH .
- Allocare memoria per contenere l'oggetto hash.
- Creare l'oggetto chiamando la funzione BCryptCreateHash . Specificare BCRYPT_HASH_REUSABLE_FLAG nel parametro dwFlags .
Eseguire l'hash dei dati chiamando la funzione BCryptHashData .
Seguire questa procedura per ottenere il valore hash:
- Ottenere le dimensioni del valore hash chiamando la funzione BCryptGetProperty per ottenere la proprietà BCRYPT_HASH_LENGTH .
- Allocare memoria per contenere il valore.
- Ottenere il valore hash chiamando BCryptFinishHash.
Per riutilizzare l'oggetto hashing con nuovi dati, passare al passaggio 3.
Per completare questa procedura, è necessario eseguire la procedura di pulizia seguente:
- Chiudere l'oggetto hash passando l'handle 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.
Duplicare un oggetto hash
In alcune circostanze, può essere utile eseguire l'hash di alcune quantità di dati comuni e quindi creare due oggetti hash separati dai dati comuni. Non è necessario creare due oggetti hash separati e hashare due volte i dati comuni per eseguire questa operazione. È possibile creare un singolo oggetto hash e aggiungere tutti i dati comuni all'oggetto hash. È quindi possibile usare la funzione BCryptDuplicateHash per creare un duplicato dell'oggetto hash originale. L'oggetto hash duplicato contiene tutte le stesse informazioni sullo stato e i dati 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 quantità probabilmente elevata 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.