前言:
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