C/C++使用本机CryptoAPI实现随机字符串

!本文可能 超过1年没有更新,今后内容也许不会被维护或者支持,部分内容可能具有时效性,涉及技术细节或者软件使用方面,本人不保证相应的兼容和可操作性。

原文发表于2008年9月13日 标题是《C/C++使用本地CryptoAPI实现随机字符串》

随机字符串的应用还是比较广泛的,比如随机的窗体标题(防止被FindWindow),随机的加密密匙,用户注册的随机密码等等。

实现一个随机字符串,很多朋友可能会想到标准的C/C++头文件提供的库函数,但是这通常是需要引入C/C++运行时支持库的,有时在做一个纯Win32 API的程序肯定要考虑程序代码的通用以及较小的体积的问题,下面我用代码说明一下如何使用本地的CryptoAPI实现随机字符串。

如果有朋友对于CryptoAPI不太了解的话可以去查看MSDN的相关说明,这套API为安全方面的加密解密等等提供了良好的支持。

由于CryptoAPI生成的不是随机的ASCII字符串,所以我们需要一个函数,其作用是将16进制转换为ASCII码。(这段代码如需在C语言环境下工作请将函数参数引用以指针方式实现)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//
//  函数功能: 将一个字节的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;
}

下面是主要函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//
//  函数功能: 产生随机的字符串
//  参数: 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日重要更新

偶尔从旧博客上翻到这篇文章,现在回想起来那时真是学汇编学傻了,其实完全没必要用汇编另外写一个艰涩难懂又不可移植的函数,而且这个函数也没有对字符串做很好的处理,存在缓冲溢出漏洞,是不安全的,我们可以简单一点,像下面那样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// 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;
}
若无特别说明,本网站文章均为原创,原则上这些文章不允许转载,但是如果阁下是出于研究学习目的可以转载到阁下的个人博客或者主页,转载遵循创作共同性“署名-非商业性使用-相同方式共享”原则,请转载时注明作者出处谢绝商业性、非署名、采集站、垃圾站或者纯粹为了流量的转载。谢谢合作!

请稍后...

发表评论

电子邮件地址不会被公开。 必填项已用*标注