CCryptHelper.h
#pragma once
#include <string>
#include <tchar.h>
#include <windows.h>
#ifdef _UNICODE
using _tstring = std::wstring;
#else
using _tstring = std::string;
#endif
// 加密辅助类
// 客户端: Windows XP 及以上系统可用
// 服务器: Windows Server 2003 及以上系统可用
class CCryptHelper
{
public:
CCryptHelper();
~CCryptHelper();
//
// @brief: 初始化
// @param: strAlgorithm 哈希算法名字符串, 可选类型如下:
// 常见可选 CALG_MD5, CALG_SHA1, CALG_SHA_256, CALG_SHA_384, CALG_SHA_512
//
// 全部支持可选如下:
// CALG_MD2, CALG_MD4, CALG_MD5, CALG_SHA, CALG_SHA1, CALG_RSA_SIGN, CALG_DSS_SIGN
// Windows XP及以上: CALG_NO_SIGN, CALG_RSA_KEYX, CALG_DES, CALG_3DES_112, CALG_3DES,
// CALG_DESX, CALG_RC2, CALG_RC4, CALG_SEAL, CALG_DH_SF, CALG_DH_EPHEM,
// CALG_AGREEDKEY_ANY, CALG_KEA_KEYX, CALG_HUGHES_MD5, CALG_SKIPJACK, CALG_TEK,
// CALG_CYLINK_MEK, CALG_SSL3_SHAMD5, CALG_SSL3_MASTER, CALG_SCHANNEL_MASTER_HASH,
// CALG_SCHANNEL_MAC_KEY, CALG_SCHANNEL_ENC_KEY, CALG_PCT1_MASTER, CALG_SSL2_MASTER,
// CALG_TLS1_MASTER, CALG_RC5, CALG_HMAC, CALG_TLS1PRF
// Windows XP及以上: CALG_HASH_REPLACE_OWF, CALG_AES_128, CALG_AES_192, CALG_AES_256, CALG_AES
// Windows SP2以上: CALG_SHA_256, CALG_SHA_384, CALG_SHA_512
// Windows Vista及以上: CALG_ECDH, CALG_ECDH_EPHEM, CALG_ECMQV, CALG_ECDSA, CALG_NULLCIPHER
// Windows 10 1607及以上: CALG_THIRDPARTY_KEY_EXCHANGE, CALG_THIRDPARTY_SIGNATURE
// CALG_THIRDPARTY_CIPHER, CALG_THIRDPARTY_HASH
//
// @ret: bool 操作是否成功
bool Initialize(
DWORD dwAlgorithm = CALG_MD5
);
//
// @brief: 反初始化
// @ret: 无
void Uninitialize();
//
// @brief: 重置
// @ret: 无
void Reset();
//
// @brief: 计算哈希值
// @param: pData 数据
// @param: ulSize 数据长度
// @ret: bool 操作是否成功
bool HashData(
const void* pData,
unsigned long long ullSize
);
//
// @brief: 获取累积的哈希值结果
// @param: bUpper 是否大写
// @ret: _tstring 哈希值字符串
_tstring FinishHash(
bool bUpper = true
);
//
// @brief: 获取文件的哈希值结果
// @param: strPath 文件路径
// @param: bUpper 是否大写
// @ret: _tstring 哈希值结果字符串
_tstring GetFileHash(
const _tstring& strPath,
bool bUpper = true
);
//
// @brief: 获取数据的哈希值结果
// @param: pData 数据指针
// @param: ulSize 数据长度
// @param: bUpper 是否大写
// @ret: _tstring 哈希值结果字符串
_tstring GetDataHash(
const void* pData,
unsigned long long ullSize,
bool bUpper = true
);
private:
//
// @brief: 计算哈希
// @param: pData 数据指针
// @param: ulSize 数据长度
// @ret: 无
bool _HashData(
const void* pData,
unsigned long ulSize
);
//
// @brief: 字节内容转字符串
// @param: pData 数据指针
// @param: nSize 数据长度
// @param: bUpper 是否大写
// @ret: _tstring 转换后的字符串
_tstring _BytesToString(
const void* pData,
size_t nSize,
bool bUpper = true
);
//
// @brief: 字符串转大小
// @param: str 字符串
// @ret: _tstring 转换后的字符串
_tstring _ToUpper(
const _tstring& str
);
//
// @brief: 多字符字符串转宽字符串
// @param: CodePage 代码页
// @param: str 字符串
// @ret: std::wstring 转换后的宽字符串
std::wstring _MultiStrToWStr(
UINT CodePage,
const std::string& str
);
//
// @brief: 字符串转宽字符串
// @param: str 字符串
// @ret: std::wstring 转换后的宽字符串
std::wstring _TStrToWStr(
const _tstring& str
);
private:
HCRYPTPROV m_hProv = NULL; // CSP 句柄
HCRYPTHASH m_hHash = NULL; // 哈希对象
DWORD m_dwAlgorithm = 0; // 算法类型
std::string m_dataBuf; // 数据缓冲(用于文件读取)
};
CCryptHelper.cpp
#include "CCryptHelper.h"
#define FILE_HASH_BLOCK_SIZE (1024 * 1024 * 4)
CCryptHelper::CCryptHelper()
:
m_hProv(NULL),
m_hHash(NULL),
m_dwAlgorithm(0)
{
}
CCryptHelper::~CCryptHelper()
{
Uninitialize();
}
bool CCryptHelper::Initialize(
DWORD dwAlgorithm/* = CALG_MD5*/
)
{
bool bSuccess = false;
if (m_dwAlgorithm == dwAlgorithm)
{
return true;
}
Uninitialize();
do
{
// 获取加密服务提供程序句柄
// https://learn.microsoft.com/zh-cn/windows/win32/api/wincrypt/nf-wincrypt-cryptacquirecontextw
if (!CryptAcquireContext(
&m_hProv,//指向 CSP 句柄的指针
NULL,//密钥容器名称
NULL,//要使用的 CSP 的名称
PROV_RSA_FULL,//要获取的提供程序的类型
CRYPT_VERIFYCONTEXT//标记值。 此参数通常设置为零
))
{
break;
}
bSuccess = true;
} while (false);
if (bSuccess)
{
m_dwAlgorithm = dwAlgorithm;
}
return bSuccess;
}
void CCryptHelper::Uninitialize()
{
if (m_hHash)
{
::CryptDestroyHash(m_hHash);
m_hHash = NULL;
}
if (m_hProv)
{
::CryptReleaseContext(m_hProv, 0);
m_hProv = NULL;
}
m_dwAlgorithm = 0;
}
void CCryptHelper::Reset()
{
if (m_hHash)
{
::CryptDestroyHash(m_hHash);
m_hHash = NULL;
}
}
bool CCryptHelper::HashData(
const void* pData,
unsigned long long ullSize
)
{
const char* pDataBegin = (const char*)pData;
const unsigned long ulMaxBlockSize = UINT32_MAX;
bool bSuccess = false;
if (0 == m_dwAlgorithm)
{
return false;
}
// 小于32位最大值则直接计算哈希值
if (ullSize <= ulMaxBlockSize)
{
return _HashData(pDataBegin, (unsigned long)ullSize);
}
// 分段计算哈希值
while (ullSize > 0)
{
unsigned long ulReadSize = (ullSize > ulMaxBlockSize) ? ulMaxBlockSize : (unsigned long)ullSize;
if (!_HashData(pDataBegin, ulReadSize))
{
break;
}
pDataBegin += ulReadSize;
ullSize -= ulReadSize;
}
return bSuccess;
}
_tstring CCryptHelper::FinishHash(
bool bUpper/* = true*/
)
{
_tstring strResult;
BYTE byteHash[MAX_PATH] = { 0 };
DWORD cbHash = sizeof(byteHash);
if (0 == m_dwAlgorithm)
{
return strResult;
}
if (NULL == m_hHash)
{
// 创建哈希处理对象
// https://learn.microsoft.com/zh-cn/windows/win32/api/wincrypt/nf-wincrypt-cryptcreatehash
::CryptCreateHash(
m_hProv, //通过调用 CryptAcquireContext 创建的 CSP 的句柄
m_dwAlgorithm, //标识要使用的哈希算法 的ALG_ID 值, 此参数的有效值因使用的 CSP 而异
0, //如果哈希算法的类型是键控哈希, 则哈希的密钥在此参数中传递
0, //标志值
&m_hHash//输出哈希对象的地址
);
}
if (NULL == m_hHash)
{
return strResult;
}
// 检索控制哈希对象操作的数据, 获取实际哈希值
// https://learn.microsoft.com/zh-cn/windows/win32/api/wincrypt/nf-wincrypt-cryptgethashparam
if (::CryptGetHashParam(
m_hHash, //要查询的哈希对象的句柄
HP_HASHVAL, //查询类型
byteHash, //指向接收指定值数据的缓冲区的指针
&cbHash, //指向指定 pbData 缓冲区大小
0 //保留供将来使用,必须为零
))
{
strResult = _BytesToString(byteHash, cbHash, bUpper);
}
// 销毁哈希对象
if (m_hHash)
{
::CryptDestroyHash(m_hHash);
m_hHash = NULL;
}
return strResult;
}
_tstring CCryptHelper::GetFileHash(
const _tstring& strPath,
bool bUpper/* = true*/
)
{
HANDLE hFile = INVALID_HANDLE_VALUE;
DWORD dwBlockSize = FILE_HASH_BLOCK_SIZE;
DWORD dwBytesRead = 0;
if (0 == m_dwAlgorithm)
{
return _tstring(_T(""));
}
do
{
// 打开文件
// https://learn.microsoft.com/zh-cn/windows/win32/api/fileapi/nf-fileapi-createfilew
hFile = CreateFile(strPath.c_str(),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if (INVALID_HANDLE_VALUE == hFile)
{
break;
}
if (m_dataBuf.empty())
{
m_dataBuf.resize(dwBlockSize);
}
// 读取文件数据
// https://learn.microsoft.com/zh-cn/windows/win32/api/fileapi/nf-fileapi-readfile
while (::ReadFile(hFile, &m_dataBuf[0], dwBlockSize, &dwBytesRead, NULL))
{
if (0 == dwBytesRead)
{
break;
}
if (!_HashData(&m_dataBuf[0], dwBytesRead))
{
break;
}
}
} while (false);
if (INVALID_HANDLE_VALUE != hFile)
{
::CloseHandle(hFile);
}
return FinishHash(bUpper);
}
_tstring CCryptHelper::GetDataHash(
const void* pData,
unsigned long long ullSize,
bool bUpper/* = true*/
)
{
_tstring strResult;
if (0 == m_dwAlgorithm)
{
return strResult;
}
if (HashData(pData, ullSize))
{
strResult = FinishHash(bUpper);
}
return strResult;
}
bool CCryptHelper::_HashData(
const void* pData,
unsigned long ulSize
)
{
bool bSuccess = false;
if (NULL == m_hHash)
{
// 创建哈希处理对象
// https://learn.microsoft.com/zh-cn/windows/win32/api/wincrypt/nf-wincrypt-cryptcreatehash
::CryptCreateHash(
m_hProv, //通过调用 CryptAcquireContext 创建的 CSP 的句柄
m_dwAlgorithm, //标识要使用的哈希算法 的ALG_ID 值, 此参数的有效值因使用的 CSP 而异
0, //如果哈希算法的类型是键控哈希, 则哈希的密钥在此参数中传递
0, //标志值
&m_hHash//输出哈希对象的地址
);
}
if (NULL == m_hHash)
{
return false;
}
// 将数据添加到指定的哈希对象
bSuccess = ::CryptHashData(
m_hHash,
(const BYTE*)pData,
ulSize,
0
);
return bSuccess;
}
_tstring CCryptHelper::_BytesToString(
const void* pData,
size_t nSize,
bool bUpper/* = true*/
)
{
const TCHAR rgbDigitsUpper[] = _T("0123456789ABCDEF");
const TCHAR rgbDigitsLower[] = _T("0123456789abcdef");
_tstring strResult(nSize * 2, 0);
LPCBYTE lpBytes = (LPCBYTE)pData;
if (bUpper)
{
for (DWORD i = 0; i < nSize; i++)
{
strResult[i * 2] = rgbDigitsUpper[lpBytes[i] >> 4];
strResult[i * 2 + 1] = rgbDigitsUpper[lpBytes[i] & 0x0F];
}
}
else
{
for (DWORD i = 0; i < nSize; i++)
{
strResult[i * 2] = rgbDigitsLower[lpBytes[i] >> 4];
strResult[i * 2 + 1] = rgbDigitsLower[lpBytes[i] & 0x0F];
}
}
return strResult;
}
_tstring CCryptHelper::_ToUpper(
const _tstring& str
)
{
_tstring strResult = str;
for (auto& item : strResult)
{
if (item >= _T('a') && item <= _T('z'))
{
item -= 0x20;
}
}
return strResult;
}
std::wstring CCryptHelper::_MultiStrToWStr(
UINT CodePage,
const std::string& str
)
{
// 计算缓冲区所需的字符长度
// https://learn.microsoft.com/zh-cn/windows/win32/api/stringapiset/nf-stringapiset-multibytetowidechar
int cchWideChar = ::MultiByteToWideChar(CodePage, 0, str.c_str(), -1, NULL, NULL);
std::wstring strResult(cchWideChar, 0);
//成功则返回写入到指示的缓冲区的字符数
size_t nConverted = ::MultiByteToWideChar(CodePage, 0, str.c_str(), (int)str.size(), &strResult[0], (int)strResult.size());
//调整内容长度
strResult.resize(nConverted);
return strResult;
}
std::wstring CCryptHelper::_TStrToWStr(
const _tstring& str
)
{
#ifdef _UNICODE
return str;
#else
return _MultiStrToWStr(CP_ACP, str);
#endif
}
main.cpp
#include <locale.h>
#include <tchar.h>
#include "Win32Utils/CTimeUtils.h"
#include "Win32Utils/CPathUtils.h"
#include "Win32Utils/CCNGHelper.h"
#include "Win32Utils/CCryptHelper.h"
int _tmain(int argc, LPCTSTR argv[])
{
UNREFERENCED_PARAMETER(argc);
UNREFERENCED_PARAMETER(argv);
::setlocale(LC_ALL, "");
unsigned long long ullStartTime;
unsigned long long ullEndTime;
CCNGHelper cngObj;
cngObj.Initialize(_T("md5"));
CCryptHelper cryptObj;
cryptObj.Initialize(CALG_MD5);
_tstring strCngHash;
_tstring strCryptHash;
system("pause");
int nCount = 1000;
while (true)
{
ullStartTime = CTimeUtils::GetCurrentTickCount();
for (int i = 0; i < nCount; i++)
{
strCngHash = cngObj.GetFileHash(CPathUtils::GetCurrentModulePath(), true);
}
ullEndTime = CTimeUtils::GetCurrentTickCount();
_tprintf(
_T("CCNGHelper MD5: %s Count: %d Cost time: %lld ms\n"),
strCngHash.c_str(),
nCount,
ullEndTime - ullStartTime
);
ullStartTime = CTimeUtils::GetCurrentTickCount();
for (int i = 0; i < nCount; i++)
{
strCryptHash = cryptObj.GetFileHash(CPathUtils::GetCurrentModulePath(), true);
}
ullEndTime = CTimeUtils::GetCurrentTickCount();
_tprintf(
_T("CCryptHelper MD5: %s Count: %d Cost time: %lld ms\n"),
strCryptHash.c_str(),
nCount,
ullEndTime - ullStartTime
);
system("pause");
}
return 0;
}