武汉 程义军
读了贵刊2000年第19期B16版“用CryptoAPI进行数据加密”一文,觉得很有所获,急忙将其键入,编译、运行,却出现了错误。当然,并不是原文提供的代码有错,而是原文的作者忽略了一个重要的问题。如果你的机器曾运行过这种数据加密程序,那当然可以正确地运行。如果是第一次才运行此程序,那将会出现问题,错误就在 CryptAcquireContext的调用上。在调用CryptEncrypt进行数据加密之前,需要取得当前机器缺省的密钥容器,而CryptAcquireContext(&hProv,NULL,NULL,PROV_RSA_FULL,0)就是用于连接缺省的CSP的,如果当前机器未曾设置过缺省的密钥容器, CryptAcquireContext 调用就会出错。因此必须为机器创建缺省的密钥容器。
创建缺省的密钥容器也需要调用CryptoAPI进行。下面介绍一种创建缺省的密钥容器的控制台程序InitUser.c。本程序的功能为:
本程序在一台机器上只须执行一次,执行之后,你将会看到在系统注册表的HKEY_CURRENT_USER\Software\microsoft下多了一个主键: Cryptography\UserKeys,并填入一些十六进制的值。之后,你就可以顺利地使用数据加密程序了,甚至可以使用数字签名了。
程序清单:
< InitUser.c >
#include < windows.h >
#include < stdio.h >
#include < stdlib.h >
#include < wincrypt.h >
//主程序不需要任何参数
void _cdecl main(void)
{
HCRYPTPROV hProv;
HCRYPTKEY hKey;
CHAR szUserName[100];
DWORD dwUserNameLen = 100;
// 试图获取缺省的密钥容器,
若失败,则创建一个缺省容器
if(!CryptAcquireContext(&hProv, NULL,
MS_DEF_PROV, PROV_RSA_FULL, 0))
{
//若获取缺省密钥容器发生错误,
就创建一个缺省密钥容器, 使用参数
CRYPT_NEWKEYSET if(!CryptAcquireContext
(&hProv,NULL,MS_DEF_PROV,
PROV_RSA_FULL,CRYPT_NEWKEYSET))
{
printf(“创建缺省密钥容器发生错误!\n");
exit(1);
}
// 取得缺省密钥容器名
if(!CryptGetProvParam(hProv, PP_CONTAINER,
szUserName, &dwUserNameLen, 0)) {
// 出错误时容器名置空
szUserName[0] = 0;
}
printf(“Create key container ‘%s'\n",szUserName);
}
// 试图获取签名密钥的名柄
if(!CryptGetUserKey(hProv, AT_SIGNATURE, &hKey))
{
if(GetLastError() == NTE_NO_KEY)
{ // 创建数字签名密钥对
printf(“Create signature key pair\n");
if(!CryptGenKey(hProv,AT_SIGNATURE,0,&hKey))
{
printf(“错误代码: %x 创建数字签名
密钥对发生错误!\n", GetLastError());
exit(1);
}
else
{
CryptDestroyKey(hKey);
}
}
else
{
printf(“错误代码: %x during
CryptGetUserKey!\n", GetLastError());
exit(1);
}
}
// 试图取得交换密钥的句柄,
没有交换密钥时,创建交换密钥
if(!CryptGetUserKey(hProv,AT_KEYEXCHANGE,&hKey))
{
if(GetLastError()==NTE_NO_KEY)
{//没有交换密钥时,创建交换密钥对
printf(“创建交换密钥\n");
if(!CryptGenKey(hProv,AT_KEYEXCHANGE,0,&hKey))
{
printf(“Error %x during CryptGenKey!\n", GetLastError());
exit(1);
}
else
{
CryptDestroyKey(hKey);
}
}
else
{
printf(“Error %x duringCryptGetUserKey!\n", GetLastError());
exit(1);
}
}
CryptReleaseContext(hProv,0); //释放句柄
printf(“OK\n");
exit(0);
}
以上程序在WinNT4.0及Win98、VC5++上编译通过,机器环境为赛扬333。若编译时发生HCRYPTPROV、HCRYPTKEY类型等错误,请打开 vc\include\wincrypt.h,将其头部的#if(_WIN32_WINNT >= 0x0400)及尾部的#endif /* _WIN32_WINNT >= 0x0400 */两行注释掉,存盘,即可编译成功。