Microsoft CryptoAPI加密技术(二)
作者:Cuick
下载本文示例源代码
上次我们讲了Microsoft CryptoAPI的构成以及会话密钥的使用。接下来我们将看一下公私密钥对的使用、HASH算法、数字签名等技术。
一、 公用密钥加密技术
公用密钥加密技术使用两个不同的密钥:公钥和私钥。私钥必须安全的保管好不能被外人知道,而公钥可以告诉任何人,只要他需要。通常公钥是以数字证书的形式发布的。
用公私密钥对中的一个密钥加密的数据只能用密钥对中的另一个密钥才能解密。也就是说用用户A的公钥加密的数据只能用A的私钥才能解密,同样,用A的私钥加密的数据只能用A的公钥才能解密。
如果用私钥签名一个消息,那么必须用与之对应的公钥去验证签名的有效性。
不幸的是公用密钥加密技术的效率非常低甚至只有对称加密的千分之一,所以不适合对大量的数据进行加密。实际上,公用密钥加密技术一般用来加密会话密钥,而数据加密可以用对称加密的方法。
好了,让我们回到Microsoft CryptoAPI。我们知道一个CSP有一个密钥库,这个密钥库有一个或多个密钥容器。而密钥容器中有什么呢?一般来说,一个密钥容器中有两对公私密钥对,一对用来加密会话密钥,而另一对用来进行数字签名,也就是大家知道的key exchange key pair和signature key pair。
那么,怎么得到这些密钥对呢?
if(CryptGetUserKey(将参数AT_SIGNATURE换成AT_KEYEXCHANGE就可以得到key exchange key pair。
hCryptProv, // 我们已经得到的CSP句柄
AT_SIGNATURE, // 这里想得到signature key pair
&hKey)) // 返回密钥句柄
{
printf("A signature key is available.\n");
}
else //取signature key pair错误
{
printf("No signature key is available.\n");
if(GetLastError() == NTE_NO_KEY) //密钥容器里不存在signature key pair
{
// 创建 signature key pair.
printf("The signature key does not exist.\n");
printf("Create a signature key pair.\n");
if(CryptGenKey(
hCryptProv, //CSP句柄
AT_SIGNATURE, //创建的密钥对类型为signature key pair
0, //key类型,这里用默认值
&hKey)) //创建成功返回新创建的密钥对的句柄
{
printf("Created a signature key pair.\n");
}
else
{
printf ("Error occurred creating a signature key.\n");
}
}
else
{
printf ("An error other than NTE_NO_KEY getting signature\key.\n");
}
} // end if
BOOL WINAPI CryptExportKey(hKey:需要被导出的密钥句柄
HCRYPTKEY hKey,
HCRYPTKEY hExpKey,
DWORD dwBlobType,
DWORD dwFlags,
BYTE* pbData,
DWORD* pdwDataLen
);
下面的例子演示如何导出密钥。
if(CryptExportKey(如果要导出用公用密钥加密技术加密的密钥,只要把API的第二个参数传入一个key exchange key pair句柄就可以了。
hKey,
NULL,
PUBLICKEYBLOB, //导出公钥
0,
NULL,
&dwBlobLen)) //返回密钥数据长度
{
printf("Size of the BLOB for the public key determined. \n");
}
else
{
printf("Error computing BLOB length.\n");
exit(1);
}
//--------------------------------------------------------------------
// Allocate memory for the pbKeyBlob.
if(pbKeyBlob = (BYTE*)malloc(dwBlobLen))
{
printf("Memory has been allocated for the BLOB. \n");
}
else
{
printf("Out of memory. \n");
exit(1);
}
//--------------------------------------------------------------------
// Do the actual exporting into the key BLOB.
if(CryptExportKey(
hKey,
NULL,
PUBLICKEYBLOB,
0,
pbKeyBlob, //返回密钥数据
&dwBlobLen)) //导出的密钥数据的长度
{
printf("Contents have been written to the BLOB. \n");
}
else
{
printf("Error exporting key.\n");
exit(1);
}
既然有了导出当然要有导入。
BOOL WINAPI CryptImportKey(这个API比较简单,这里就不举例说明了,在以后的例子里会看到。
HCRYPTPROV hProv, //CSP句柄
BYTE* pbData, //要导入的密钥数据
DWORD dwDataLen, //数据长度
HCRYPTKEY hPubKey, //如果数据是被加密的这里输入解密用的密钥句柄
DWORD dwFlags, //标志位
HCRYPTKEY* phKey //导入后返回的密钥句柄
);
二、 HASH
Hash简单点讲就是把任意一段数据经过某种算法生成一段唯一的固定长度的数据。也叫做摘要。为了确保数据A免受意外或者故意(恶意)的修改,往往用这段数据A产生一个hash数据一起发送出去,接收方可以通过相同的hash算法用这段接收到的数据A产生一个新的hash数据并与接收到的hash数据比较,来验证数据A是否为真实完整的数据。
下面的API用来创建hash对象
BOOL WINAPI CryptCreateHash(我们已经得到hash对象了,下面就找点数据试试,咱也去哈一下,当然这里可不是哈日哈韩的哈,更不是哈巴狗的哈,嘿嘿。Let’s go!!
HCRYPTPROV hProv, //CSP句柄
ALG_ID Algid, //选择hash算法,比如CALG_MD5等
HCRYPTKEY hKey, //HMAC 和MAC算法时有用
DWORD dwFlags, //保留,传入0即可
HCRYPTHASH* phHash //返回hash句柄
);
if(CryptCreateHash(
hCryptProv,
CALG_MD5,
0,
0,
&hHash))
{
printf("An empty hash object has been created. \n");
}
else
{
printf("Error during CryptBeginHash!\n");
exit(1);
}
// Insert code that uses the hash object here.
//--------------------------------------------------------------------
// After processing, hHash must be released.
if(hHash)
CryptDestroyHash(hHash); //释放句柄
BOOL WINAPI CryptHashData(下面代码:
HCRYPTHASH hHash, //hash对象
BYTE* pbData, //被hash的数据
DWORD dwDataLen, //数据的长度
DWORD dwFlags //微软的CSP这个值会被忽略
);
BYTE *pbBuffer= (BYTE *)"The data that is to be hashed.";
DWORD dwBufferLen = strlen((char *)pbBuffer)+1;
if(CryptHashData(
hHash,
pbBuffer,
dwBufferLen,
0))
{
printf("The data buffer has been added to the hash.\n");
}
else
{
printf("Error during CryptHashData.\n");
exit(1);
}
BYTE *pbHash;
BYTE *pbHashSize;
DWORD dwHashLen = sizeof(DWORD);
DWORD i;
if(!(pbHashSize =(BYTE *) malloc(dwHashLen)))
MyHandleError("Memory allocation failed.");
//下面的这次调用我没搞清楚:( 我怎么觉得没有必要!!
if(CryptGetHashParam(
hHash,
HP_HASHSIZE, //取hash数据的大小
pbHashSize, //输出hash数据大小的缓冲区
&dwHashLen, //缓冲区大小
0))
{
// It worked. Free pbHashSize.
free(pbHashSize);
}
else
{
MyHandleError("CryptGetHashParam failed to get size.");
}
if(CryptGetHashParam(
hHash,
HP_HASHVAL, //取hash值
NULL, //设为NULL,在dwHashLen返回需要的输出缓冲区大小
&dwHashLen, //输出缓冲区大小
0))
{
// It worked. Do nothing.
}
else
{
MyHandleError("CryptGetHashParam failed to get length.");
}
if(pbHash = (BYTE*)malloc(dwHashLen))
{
// It worked. Do nothing.
}
else
{
MyHandleError("Allocation failed.");
}
if(CryptGetHashParam(
hHash,
HP_HASHVAL, //取hash值
pbHash, //返回Hash数据
&dwHashLen, //hash数据长度
0))
{
// Print the hash value.
printf("The hash is: ");
for(i = 0 ; i < dwHashLen ; i++)
{
printf("%2.2x ",pbHash[i]);
}
printf("\n");
}
else
{
MyHandleError("Error during reading hash value.");
}
free(pbHash);