前言:
Crypto是微软的加密API,如果看懂了,使用起来是很简单的一件事,不过就是最开始没有看懂,被虐了两天。然后又被其他问题给虐了两天。最后做出来的东西也不是让自己十分满意。不过还好,最后的结果还不算太糟。
本想对代码进行一次整理,写一个demo代码,不过现在有些慵懒了,还是随便贴些笔记好了。
PS:
发现Delphi盒子要卖了。这似乎也验证了一句话,有商业价值的东西才会有持续的生命力。
Crypto 加密的基本流程
- 创建/获取一个密码容器CSP
- 创建/获取/导入一个密钥
- 使用密钥进行加密/解密
加密具体流程:
- 创建/获取一个密码容器CSP。这一部分基本上所有程序都一样,直接复制过来到程序里就可以了。
//以下获得一个CSP句柄
if(CryptAcquireContext(
&hProv, //out 密码容器
NULL, //NULL表示使用默认密钥容器,默认密钥容器名为用户登陆名。
NULL,
PROV_RSA_FULL, //in 使用RSA密钥
0))
{
printf("A cryptographic provider has been acquired. \n");
}
else//密钥容器不存在
{
if(CryptAcquireContext(
&hProv,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_NEWKEYSET))//创建密钥容器
{
//创建密钥容器成功,并得到CSP句柄
printf("A new key container has been created.\n");
}
else
{
HandleError("Could not create a new key container.\n");
}
}
- 创建/获取一个密钥。这里有些程序里会创建一个sessionKey会话密钥用于对称加密,这里创建的是非对称加密的密钥。
//--------------------------------------------------------------------
// 从密钥容器中取交换密钥
if(CryptGetUserKey(
hProv, //CSP句柄,也就是在一里面创建的,密码容器。
AT_KEYEXCHANGE, //密钥的类型,这里指名的是交换密钥。还有一个AT_SIGNATURE,这个是数字签名用的。
&hKey)) //out 获取到的密钥
{
printf("The signature key has been acquired. \n");
}
else
{
if(GetLastError() == NTE_NO_KEY) //密钥容器里不存在key pair创建之
{
if(CryptGenKey(
hProv, //in CSP句柄
AT_SIGNATURE, //in 创建的密钥对类型为signature key pair
0, //key类型,这里用默认值
&hKey)) //out 创建成功返回新创建的密钥对的句柄
{
printf("Created a signature key pair.\n");
}
else
{
MyHandleError("Error occurred creating a signature key.\n");
}
}
else
{
MyHandleError("Error during CryptGetUserKey for signkey.");
}
}
- 使用密钥进行加密
//--------------------------------------------------------------------
// 加密数据
if(!CryptEncrypt(
hKey, //in 密钥
0, //如果数据同时进行散列和加密,这里传入一个散列对象
TRUE, //如果是最后一个被加密的块,输入TRUE.如果不是输入FALSE.
//这里通过判断是否到文件尾来决定是否为最后一块。
0, //保留
pbBuffer, // in/out输入被加密数据,输出加密后的数据。将pbBuffer分配大一些,防止长度不够。
&dwCount, // in/out输入被加密数据实际长度,输出加密后数据长度。这个需要根据pbBuffer的实际输入长度进行计算(strlen?),不然不能正常运行。
dwBufferLen)) //in pbBuffer的大小。这里填大点,
{
HandleError("Error during CryptEncrypt. \n");
}
到这里加密就已经做完了,加密后的数据保存在pbBuffer中。
- 导出公/私钥
私钥在加密的时候需要,以后使用的时候不再生成,直接导入。(备注:如果需要导出私钥需要在创建密钥时候设置参数,具体见MSDN)
解密的时候需要用到公钥,需要将公钥分发给解密用户。
//--------------------------------------------------------------------
// 因为接收消息者要验证数字签名,所以要导出公钥给接收者。
if(CryptExportKey(
hKey, //in 密钥句柄
NULL,
PUBLICKEYBLOB,// out 公钥输出数据。在导出后,需要将公钥的数据保存成文件等,以便分发。
0,
NULL,
&dwBlobLen)) //out 得到公钥的大小
{
printf("Size of the BLOB for the public key determined. \n");
}
else
{
MyHandleError("Error computing BLOB length.");
}
- 销毁容器和Key
在完成加密后,需要对key和容器进行销毁,相关函数如下。
if(hKey)
CryptDestroyKey(hKey);
if(hCryptProv)
CryptReleaseContext(hCryptProv, 0);
解密的具体流程:
- 同加密流程
- 导入解密用的公钥。
HCRYPTKEY hPubKey;
if(CryptImportKey(
hProv,//in CSP密码容器
pbKeyBlob,//in 需要导入的公钥数据(在加密的时候导出的公钥,加密中的4)
dwBlobLen,//in 公钥数据的长度
0,
0,
&hPubKey))//out 公钥导入得到的公钥句柄
{
printf("The key has been imported.\n");
}
else
{
MyHandleError("Public key import failed.");
}
- 解密
解密的参数和加密的参数基本相同。
//--------------------------------------------------------------------
// Decrypt data.
if(!CryptDecrypt(
hPubKey, //in 解密用的公钥,也就是在2中导入的公钥
0,
TRUE, //如果是最后一个被加密的块,输入TRUE.如果不是输入FALSE.
0,
pbBuffer, // in/out输入被解密数据,输出加密后的数据。将pbBuffer分配大一些,防止长度不够。
&dwCount)) // in/out输入被解密数据实际长度,输出加密后数据长度。这个需要根据pbBuffer的实际输入长度进行计算(strlen?),不然不能正常运行。
{
HandleError("Error during CryptDecrypt!");
}
- 销毁容器和Key
同加密
相关参考资料:
学习CRYPTOAPI第一天(这个网站还有另外的两篇 二/三天) http://www.wangchao.org/bbsdetail_66820.html
VC知识库文档中心(Microsoft CryptoAPI加密技术 一/二) http://www.vckbase.com/document/listdoc.asp?mclsid=&sclsid=109&page=1
Crypto API 学习笔记一/二/三(另外两篇baidu下) http://www.pediy.com/bbshtml/bbs8/pediy8-364.htm