使用 CNG 创建哈希
哈希是对数据块执行的单向操作,用于创建表示数据内容的唯一哈希值。 无论何时执行哈希,对相同数据执行的相同 哈希算法 将始终生成相同的哈希值。 如果任何数据发生更改,哈希值将相应地更改。
哈希对加密数据没有用,因为它们不用于从哈希值重现原始数据。 哈希在与非对称签名算法一起使用时最适用于验证数据的完整性。 例如,如果对文本消息进行哈希处理,对哈希进行签名,并将已签名哈希值与原始消息一起包含,则接收方可以验证已签名哈希,为收到的消息创建哈希值,然后将此哈希值与原始消息附带的已签名哈希值进行比较。 如果两个哈希值相同,则收件人可以合理地确保原始邮件未被修改。
哈希值的大小对于特定哈希算法是固定的。 这意味着,无论数据块有多大或多小,哈希值都将始终是相同的大小。 例如,SHA256 哈希算法的哈希值大小为 256 位。
创建哈希对象
若要使用 CNG 创建哈希,请执行以下步骤:
打开支持所需算法的算法提供程序。 典型的哈希算法包括 MD2、MD4、MD5、SHA-1 和 SHA256。 调用 BCryptOpenAlgorithmProvider 函数并在 pszAlgId 参数中指定适当的算法标识符。 函数将句柄返回到提供程序。
执行以下步骤以创建哈希对象:
- 通过调用 BCryptGetProperty 函数检索 BCRYPT_OBJECT_LENGTH 属性来获取对象的大小。
- 分配内存以保存哈希对象。
- 通过调用 BCryptCreateHash 函数创建 对象。
对数据进行哈希处理。 这涉及到调用 BCryptHashData 函数一次或多次。 每个调用都会将指定的数据追加到哈希中。
执行以下步骤以获取哈希值:
- 通过调用 BCryptGetProperty 函数获取 BCRYPT_HASH_LENGTH 属性来检索值的大小。
- 分配内存以保存值。
- 通过调用 BCryptFinishHash 函数检索哈希值。 调用此函数后,哈希对象不再有效。
若要完成此过程,必须执行以下清理步骤:
通过将哈希句柄传递给 BCryptDestroyHash 函数来关闭哈希对象。
释放为哈希对象分配的内存。
如果不再创建哈希对象,请通过将提供程序句柄传递给 BCryptCloseAlgorithmProvider 函数来关闭算法提供程序。
如果要创建更多哈希对象,建议重复使用算法提供程序,而不是多次创建和销毁同一类型的算法提供程序。
使用完哈希值内存后,请释放它。
以下示例演示如何使用 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);
}
}
创建可重用哈希对象
从Windows 8和Windows Server 2012开始,可以为需要快速连续计算多个哈希或 HMAC 的方案创建可重用哈希对象。 为此,请在调用 BCryptOpenAlgorithmProvider 函数时指定BCRYPT_HASH_REUSABLE_FLAG。 所有 Microsoft 哈希算法提供程序都支持此标志。 使用此标志创建的哈希对象可以在调用 BCryptFinishHash 后立即重复使用,就像它是通过调用 BCryptCreateHash 重新创建的一样。 执行以下步骤以创建可重用的哈希对象:
打开支持所需哈希算法的算法提供程序。 调用 BCryptOpenAlgorithmProvider 函数并在 pszAlgId 参数中指定适当的算法标识符,并在 dwFlags 参数中BCRYPT_HASH_REUSABLE_FLAG。 函数将句柄返回到提供程序。
执行以下步骤以创建哈希对象:
- 通过调用 BCryptGetProperty 函数检索 BCRYPT_OBJECT_LENGTH 属性来获取对象的大小。
- 分配内存以保存哈希对象。
- 通过调用 BCryptCreateHash 函数创建 对象。 在 dwFlags 参数中指定BCRYPT_HASH_REUSABLE_FLAG。
通过调用 BCryptHashData 函数对数据进行哈希处理。
执行以下步骤以获取哈希值:
- 通过调用 BCryptGetProperty 函数获取 BCRYPT_HASH_LENGTH 属性来获取哈希值的大小。
- 分配内存以保存值。
- 通过调用 BCryptFinishHash 获取哈希值。
若要对新数据重复使用哈希对象,请转到步骤 3。
若要完成此过程,必须执行以下清理步骤:
- 通过将哈希句柄传递给 BCryptDestroyHash 函数来关闭哈希对象。
- 释放为哈希对象分配的内存。
- 如果不再创建哈希对象,请通过将提供程序句柄传递给 BCryptCloseAlgorithmProvider 函数来关闭算法提供程序。
- 使用完哈希值内存后,请释放它。
复制哈希对象
在某些情况下,对一定数量的常见数据进行哈希处理,然后根据公共数据创建两个单独的哈希对象可能很有用。 无需创建两个单独的哈希对象,对公共数据进行两次哈希处理就可完成此操作。 可以创建单个哈希对象,并将所有公共数据添加到哈希对象。 然后,可以使用 BCryptDuplicateHash 函数创建原始哈希对象的副本。 重复哈希对象包含与原始对象相同的所有状态信息和哈希数据,但它是完全独立的哈希对象。 现在可以向每个哈希对象添加唯一数据,并获取哈希值,如示例中所示。 对可能大量的常见数据进行哈希处理时,此方法非常有用。 只需将公共数据添加到原始哈希一次,然后就可以复制哈希对象来获取唯一的哈希对象。