一、Hash算法
(一)散列算法,主要执行步骤:
1、获取算法提供程序
2、获取Hash对象的大小
3、为Hash对象分配空间
4、获取Hash值所需空间大小
5、分配Hash值所需空间
6、创建Hash对象
7、对数据执行哈希处理
8、获取Hash值
9、关闭Hash对象
10、关闭算法提供程序
11、清理(释放不需要的堆空间)
其中,2、3两不在Win7以后的系统中可以不执行,其Hash对象会由CNG自行管理,而4、5只需要在第8步前执行就可以
需要使用到的函数:
//打开支持所需算法的算法提供程序
NTSTATUS BCryptOpenAlgorithmProvider(
[out] BCRYPT_ALG_HANDLE *phAlgorithm,
[in] LPCWSTR pszAlgId,
[in] LPCWSTR pszImplementation,
[in] ULONG dwFlags
);
//获取Hash对象的大小
NTSTATUS BCryptGetProperty(
[in] BCRYPT_HANDLE hObject,
[in] LPCWSTR pszProperty,
[out] PUCHAR pbOutput,
[in] ULONG cbOutput,
[out] ULONG *pcbResult,
[in] ULONG dwFlags
);
//创建Hash对象
NTSTATUS BCryptCreateHash(
[in, out] BCRYPT_ALG_HANDLE hAlgorithm,
[out] BCRYPT_HASH_HANDLE *phHash,
[out] PUCHAR pbHashObject,
[in, optional] ULONG cbHashObject,
[in, optional] PUCHAR pbSecret,
[in] ULONG cbSecret,
[in] ULONG dwFlags
);
//对数据进行哈希处理,可多次调用
NTSTATUS BCryptHashData(
[in, out] BCRYPT_HASH_HANDLE hHash,
[in] PUCHAR pbInput,
[in] ULONG cbInput,
[in] ULONG dwFlags
);
//获取数据哈希值
NTSTATUS BCryptFinishHash(
[in, out] BCRYPT_HASH_HANDLE hHash,
[out] PUCHAR pbOutput,
[in] ULONG cbOutput,
[in] ULONG dwFlags
);
//关闭Hash对象
NTSTATUS BCryptDestroyHash(
[in, out] BCRYPT_HASH_HANDLE hHash
);
//关闭算法提供程序
NTSTATUS BCryptCloseAlgorithmProvider(
[in, out] BCRYPT_ALG_HANDLE hAlgorithm,
[in] ULONG dwFlags
);
实例:(代码根据官方演示案例修改而来,用于演示流程,因此去掉了返回校验过程)
#include <windows.h>
#include <bcrypt.h>
static const BYTE rgbMsg[] =
{
0x61, 0x62, 0x63
};
int main()
{
BCRYPT_ALG_HANDLE hAlg = NULL;
BCRYPT_HASH_HANDLE hHash = NULL;
DWORD cbData = 0,
cbHash = 0,
cbHashObject = 0;
PBYTE pbHashObject = NULL;
PBYTE pbHash = NULL;
//打开支持所需算法的算法提供程序,获取算法句柄
BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA256_ALGORITHM, NULL, 0);
//获取Hash对象的大小,Win7后可以不自行管理Hash对象,此步骤可以不做
BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH, (PBYTE)&cbHashObject, sizeof(DWORD), &cbData, 0);
//为Hash对象分配空间,Win7后可以不自行管理Hash对象,此步骤可以不做
pbHashObject = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbHashObject);
//获取Hash值所需空间大小
BCryptGetProperty(hAlg, BCRYPT_HASH_LENGTH, (PBYTE)&cbHash, sizeof(DWORD), &cbData, 0);
//分配Hash值所需空间
pbHash = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbHash);
//创建Hash对象
BCryptCreateHash(hAlg, &hHash, pbHashObject, cbHashObject, NULL, 0, 0);
//Win7后可以不需要pbHashObject和cbHashObject,如下
//BCryptCreateHash(hAlg, &hHash, NULL, 0, NULL, 0, 0);
//对数据进行哈希处理
BCryptHashData(hHash, (PBYTE)rgbMsg, sizeof(rgbMsg), 0);
//获取哈希值
BCryptFinishHash(hHash, pbHash, cbHash, 0);
//关闭Hash对象
BCryptDestroyHash(hHash);
//关闭算法提供程序
BCryptCloseAlgorithmProvider(hAlg,0);
//清理
HeapFree(GetProcessHeap(), 0, pbHashObject);
HeapFree(GetProcessHeap(), 0, pbHash);
return 0;
}
BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA256_ALGORITHM, NULL, 0);
BCryptOpenAlgorithmProvider函数中对于Hash比较有用的就是dwFlags参数,主要有两个值
BCRYPT_ALG_HANDLE_HMAC_FLAG和BCRYPT_HASH_REUSABLE_FLAG
其中BCRYPT_HASH_REUSABLE_FLAG也可以用于BCryptCreateHash函数,作用是在算法调用完BCryptFinishHash后,使用此标志的Hash对象可以再次重复使用,而不使用的则必须直接调用BCryptDestroyHash销毁对象。而此标志作用于BCryptOpenAlgorithmProvider和BCryptCreateHash函数的区别是,前者作用于全局,也就是基于BCryptOpenAlgorithmProvider所生成的hAlg而创建的Hash对象都会拥有BCRYPT_HASH_REUSABLE_FLAG,而BCryptCreateHash则只对当前对象有效。
BCRYPT_ALG_HANDLE_HMAC_FLAG则代表当前算法是基于哈希的消息身份验证代码,使用此标志的后,需要在BCryptCreateHash的pbSecret和cbSecret参数分别输入指向包含用于hash或MAC的认证密钥的缓冲区的指针,及其大小
//获取哈希值
BCryptFinishHash(hHash, pbHash, cbHash, 0);
注意:
此处有个容易忽略的关键点,BCryptFinishHash函数的cbOutput参数,也就是上方代码的cbHash,其值必须严格等于BCryptGetProperty获取BCRYPT_HASH_LENGTH的值,小了自然不行,大了也不行,依然会报参数错误STATUS_INVALID_PARAMETER,其介绍中也提到
This includes the case where cbOutput is not the same size as the hash.
当然,这个散列值的大小对于不同算法其值实际上是固定的
算法 | 哈希值长度 (位) | 哈希值长度 (字节) |
---|---|---|
MD2 | 128 | 16 |
MD4 | 128 | 16 |
MD5 | 128 | 16 |
SHA-1 | 160 | 20 |
SHA-256 | 256 | 32 |
SHA-384 | 384 | 48 |
SHA-512 | 512 | 64 |
(二)HMAC(Hash-based Message Authentication Code):
具体流程和普通Hash一样,区别在于获取算法句柄时添加BCRYPT_ALG_HANDLE_HMAC_FLAG标志,在BCryptCreateHash中输入pbSecret和cbSecret参数
#include <windows.h>
#include <bcrypt.h>
static const BYTE rgbMsg[] =
{
0x61, 0x62, 0x63
};
static const BYTE rgbSecret[] =
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
};
int main()
{
BCRYPT_ALG_HANDLE hAlg = NULL;
BCRYPT_HASH_HANDLE hHash = NULL;
DWORD cbData = 0,
cbHash = 0;
PBYTE pbHash = NULL;
//打开支持所需算法的算法提供程序,获取算法句柄
BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA256_ALGORITHM, NULL, BCRYPT_ALG_HANDLE_HMAC_FLAG);
//获取Hash值所需空间大小
BCryptGetProperty(hAlg, BCRYPT_HASH_LENGTH, (PBYTE)&cbHash, sizeof(DWORD), &cbData, 0);
//分配Hash值所需空间
pbHash = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbHash);
//创建Hash对象,Win7后可以不需要pbHashObject和cbHashObject
BCryptCreateHash(hAlg, &hHash, NULL, 0, rgbSecret, sizeof(rgbSecret), 0);
//对数据进行哈希处理
BCryptHashData(hHash, (PBYTE)rgbMsg, sizeof(rgbMsg), 0);
//获取哈希值
BCryptFinishHash(hHash, pbHash, cbHash, 0);
//关闭Hash对象
BCryptDestroyHash(hHash);
//关闭算法提供程序
BCryptCloseAlgorithmProvider(hAlg,0);
//清理
HeapFree(GetProcessHeap(), 0, pbHashObject);
HeapFree(GetProcessHeap(), 0, pbHash);
return 0;
}
复制Hash对象:
//复制现有的Hash或消息身份验证(MAC)对象。复制对象包含复制时原始对象中包含的所有状态和数据。
NTSTATUS BCryptDuplicateHash(
[in] BCRYPT_HASH_HANDLE hHash,
[out] BCRYPT_HASH_HANDLE *phNewHash,
[out] PUCHAR pbHashObject,
[in] ULONG cbHashObject,
[in] ULONG dwFlags
);
如果需要的哈希值是由多个部分组成的,且,其拥有公共部分时,就可以使用此函数,先将公共部分或者通用数据添加(BCryptHashData)到Hash对象中,而后对Hash对象进行的复制,复制的Hash对象包含与原始相同的状态信息和哈希数据,但它是完全独立的Hash对象。其后可以再使用复制对象添加需要计算哈希值的后续数据(BCryptHashData)直至获取最后的哈希值(BCryptFinishHash)
二、随机数
随机数就比较简单了,可以使用BCryptOpenAlgorithmProvider获取算法句柄,也可以使用BCRYPT_USE_SYSTEM_PREFERRED_RNG标志,使用系统选用的随机数算法
#include <windows.h>
#include <bcrypt.h>
int main()
{
BCRYPT_ALG_HANDLE hAlg = NULL;
BYTE buffer[128];
//打开支持所需算法的算法提供程序,获取算法句柄
BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM, NULL, 0);
//创建随机数
BCryptGenRandom(hAlg, buffer, sizeof(buffer), 0);
//关闭算法提供程序
BCryptCloseAlgorithmProvider(hAlg,0);
return 0;
}
或者用BCRYPT_USE_SYSTEM_PREFERRED_RNG标志,此标志仅适用于与Windows Vista SP2及以后版本
#include <windows.h>
#include <bcrypt.h>
int main()
{
BCRYPT_ALG_HANDLE hAlg = NULL;
BYTE buffer[128];
//创建随机数
BCryptGenRandom(NULL, buffer, sizeof(buffer), BCRYPT_USE_SYSTEM_PREFERRED_RNG);
return 0;
}