C/C++使用本机CryptoAPI实现随机字符串
提醒:本页面将不再更新、维护或者支持,文章、评论所叙述内容存在时效性,涉及技术细节或者软件使用方面不保证能够完全有效可操作,请谨慎参考!
原文发表于2008年9月13日 标题是《C/C++使用本地CryptoAPI实现随机字符串》
随机字符串的应用还是比较广泛的,比如随机的窗体标题(防止被FindWindow),随机的加密密匙,用户注册的随机密码等等。
实现一个随机字符串,很多朋友可能会想到标准的C/C++头文件提供的库函数,但是这通常是需要引入C/C++运行时支持库的,有时在做一个纯Win32 API的程序肯定要考虑程序代码的通用以及较小的体积的问题,下面我用代码说明一下如何使用本地的CryptoAPI实现随机字符串。
如果有朋友对于CryptoAPI不太了解的话可以去查看MSDN的相关说明,这套API为安全方面的加密解密等等提供了良好的支持。
由于CryptoAPI生成的不是随机的ASCII字符串,所以我们需要一个函数,其作用是将16进制转换为ASCII码。(这段代码如需在C语言环境下工作请将函数参数引用以指针方式实现)
//
// 函数功能: 将一个字节的16进制的高位和低位变为ASCII码输出
// 参数: HighPart -- 输出的高位的ASCII码
// LowPart -- 输出的低位ASCII码
// lpbSrc -- 传入的字节数据
// 返回: VOID
//
void HexToChar(
TCHAR &HighPart,
TCHAR &LowPart,
const LPBYTE &lpbSrc
)
{
BYTE bytHPart = *(lpbSrc) >> 4; // 右移4位,将高位移到低位
BYTE bytLPart = *(lpbSrc) & 0xf; // 屏蔽高位,保留低位
__asm
{
mov al, bytLPart
mov ah, bytHPart
cmp al, 0ah
jb ASM_TAG1
add al, 07h
ASM_TAG1:
add al, 30h
mov bytLPart, al
cmp ah, 0ah
jb ASM_TAG2
add ah, 07h
ASM_TAG2:
add ah, 30h
mov bytHPart, ah
}
HighPart = (TCHAR)bytHPart;
LowPart = (TCHAR)bytLPart;
}
下面是主要函数。
//
// 函数功能: 产生随机的字符串
// 参数: Buf -- 输出字符串的数组地址
// MaxLength -- 随机字符串最大长度
//
BOOL GetRandString(LPTSTR Buf, DWORD MaxLength)
{
HCRYPTPROV hCryptProv=0;
BOOL retVal;
DWORD i, j=0;
TCHAR chFri,chSec;
LPBYTE pbCode = NULL;
HANDLE hHeap = NULL;
retVal=CryptAcquireContext(
&hCryptProv,
NULL,
NULL,
PROV_RSA_FULL,
0);
if(!retVal) {
goto exit;
}
hHeap = GetProcessHeap(); // 获得当前进程的堆
if(NULL == hHeap) {
goto exit;
}
if(NULL == (pbCode = (LPBYTE)HeapAlloc(
hHeap,HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY ,
sizeof(BYTE)*MaxLength))) // 使用HeapAlloc在堆上分配空间
{
goto exit;
}
retVal=CryptGenRandom(
hCryptProv,
MaxLength,
pbCode); // 产生随机值
if(!retVal) {
goto exit;
}
// 依次读取随机值并生成ASCII码
for(i = 0 ; i < MaxLength ; i++) {
HexToChar(chFri, chSec, pbCode+i);
*(Buf + j)=chFri;
*(Buf + j + 1)=chSec;
j+=2;
}
*(Buf + j)='\0';
exit:
if(hCryptProv)
CryptReleaseContext(hCryptProv,0);
if(NULL != hHeap)
HeapFree(hHeap, HEAP_NO_SERIALIZE, (LPBYTE)pbCode);
return retVal;
}
2011年3月28日重要更新
偶尔从旧博客上翻到这篇文章,现在回想起来那时真是学汇编学傻了,其实完全没必要用汇编另外写一个艰涩难懂又不可移植的函数,而且这个函数也没有对字符串做很好的处理, 存在缓冲溢出漏洞,是不安全的 ,我们可以简单一点,像下面那样:
// lpszBuffer -- 字符串缓冲区指针
// cchSize -- 字符串缓冲区能容纳字符的个数
// nMaxLength -- 随机字符串的最大长度
LPTSTR StringFromRandom(
LPTSTR lpszBuffer, int cchSize, int nMaxLength
)
{
HCRYPTPROV hCryptProv = 0U;
LPBYTE lpbBuffer = NULL;
int i;
__try {
if (!CryptAcquireContext(
&hCryptProv,
NULL,
NULL,
PROV_RSA_FULL,
0
))
__leave;
lpbBuffer = (LPBYTE)malloc(
sizeof(BYTE) * nMaxLength
);
if (NULL == lpbBuffer)
__leave;
if (!CryptGenRandom(
hCryptProv,
sizeof(BYTE) * nMaxLength,
lpbBuffer))
__leave;
memset(lpszBuffer, 0, sizeof(TCHAR) * cchSize);
for (i = 0; i<__min(cchSize - 1, nMaxLength); i++) {
StringCchPrintf(
lpszBuffer + i,
cchSize - i,
TEXT("%X"),
*(lpbBuffer+i));
}
} __finally {
if (lpbBuffer)
free(lpbBuffer);
if(hCryptProv)
CryptReleaseContext(hCryptProv,0);
}
return lpszBuffer;
}
[...] C/C++使用本机CryptoAPI实现随机字符串 [...]