Microsoft CryptoAPI加密技术(一)
http://www.vckbase.com/index.php/wv/716.html
在這個信息爆炸的時代,我們不得不對信息的安全提高警惕。加密作為保障數據信息安全的一種方式,越來越受到人們的關注。
下面,我將把自己對Microsoft CryptoAPI的一些膚淺的理解與大家共享,有什么不妥之處望不吝賜教。
一、 加密方法:
當初,計算機的研究就是為了破解德國人的密碼,人們并沒有想到計算機給今天帶來的信息革命。隨著計算機的發展,運算能力的增強,密碼學已經取得了巨大的進展。大體來說有以下幾種形式。
1、 公用密鑰加密技術
加密和解密使用不同的密鑰,分別叫做“公鑰”和“私鑰”。顧名思義,“私鑰”就是不能讓別人知道的,而“公鑰”就是可以公開的。這兩個必須配對使用,用公鑰加密的數據必須用與其對應的私鑰才能解開。這種技術安全性高,得到廣泛運用,但是效率太低。
2、 對稱密鑰加密技術
要求加密和解密過程使用相同的密鑰,這樣,密鑰必須只能被加解密雙方知道,否則就不安全。這種技術安全性不高,但是效率高。
3、 結合公用和對稱密鑰加密技術
公鑰加密技術以速度為代價換取了高安全性,而對稱加密以低安全換取高性能,所以另一種常見的加密方法就是結合以上兩種技術。
用對稱加密算法對數據進行加密,然后使用更安全的但效率更低的公鑰加密算法對對稱密鑰進行加密。
4、 數字簽名和鑒別
就是對已經加密的數據“簽名”,這樣接收者可以知道加密的數據的來源,以及是否被更改。
二、 CryptoAPI
微軟的CryptoAPI是PKI推薦使用的加密 API。其功能是為應用程序開發者提供在Win32環境下使用加密、驗證等安全服務時的標準加密接口。CryptoAPI處于應用程序和CSP(cryptographic service provider)之間(見圖一)。
?
CryptoAPI的編程模型同Windows系統的圖形設備接口 GDI比較類似,其中加密服務提供者CSP等同于圖形設備驅動程序 ,加密硬件(可選)等同于圖形硬件,其上層的應用程序也類似,都不需要同設備驅動程序和硬件直接打交道。
CryptoAPI共有五部分組成:簡單消息函數(Simplified Message Functions)、低層消息函數(Low-level Message Functions)、基本加密函數(Base Cryptographic Functions)、證書編解碼函數(Certificate Encode/Decode Functions)和證書庫管理函數(Certificate Store Functions)。其中前三者可用于對敏感信息進行加密或簽名處理,可保證網絡傳輸信心的私有性;后兩者通過對證書的使用,可保證網絡信息交流中的認證性。
三、 CSP
看到這里,大家也許對CSP還比較迷惑。其實CSP是真正實行加密的獨立模塊,他既可以由軟件實現也可以由硬件實現。但是他必須符合CryptoAPI接口的規范。
每個CSP都有一個名字和一個類型。每個CSP的名字是唯一的,這樣便于CryptoAPI找到對應的CSP。目前已經有9種CSP類型,并且還在增長。下表列出出它們支持的密鑰交換算法、簽名算法、對稱加密算法和Hash算法。
(表一)
| CSP類型 | 交換算法 | 簽名算法 | 對稱加密算法 | Hash算法 |
| PROV_RSA_FULL | RSA | RSA | RC2 RC4 | MD5 SHA |
| PROV_RSA_SIG | none | RSA | none | MD5 SHA |
| PROV_RSA_SCHANNEL | RSA | RSA | RC4 DES Triple DES | MD5 SHA |
| PROV_DSS | DSS | none | DSS | MD5 SHA |
| PROV_DSS_DH | DH | DSS | CYLINK_MEK | MD5 SHA |
| PROV_DH_SCHANNEL | DH | DSS | DES Triple DES | MD5 SHA |
| PROV_FORTEZZA | KEA | DSS | Skipjack | SHA |
| PROV_MS_EXCHANGE | RSA | RSA | CAST | MD5 |
| PROV_SSL | RSA | RSA | Varies | Varies |
從圖一可以看到,每個CSP有一個密鑰庫,密鑰庫用于存儲密鑰。而每個密鑰庫包括一個或多個密鑰容器(Key Containers)。每個密鑰容器中含屬于一個特定用戶的所有密鑰對。每個密鑰容器被賦予一個唯一的名字。在銷毀密鑰容器前CSP將永久保存每一個密鑰容器,包括保存每個密鑰容器中的公/私鑰對(見圖二)。
四、 創建密鑰容器,得到CSP句柄
說了這么多只是一些理論性的東西,后面將詳細介紹一下Microsoft CryptoAPI的使用方法。
我們已經提過,每一個CSP都有一個名字和一個類型,并且名字保證唯一。所以可以通過名字和類型得到一個CSP。然而,要想加密肯定需要密鑰,那么密鑰放哪里呢?對了,就放在密鑰容器。(有人會問,密碼庫有什么用?其實密鑰庫是在安裝CSP的時候已經存在了,他與CSP是相對應的。)但是密鑰容器并不是一開始就存在的,需要用戶去創建。下面的代碼實現以上功能(得到CSP即密碼容器)。
view source print? 01.if(CryptAcquireContext( 02.&hCryptProv,?????????????? // 返回CSP句柄 03.UserName,????????????????? // 密碼容器名 04.NULL,????????????????????? // NULL時使用默認CSP名(微軟RSA Base Provider) 05.PROV_RSA_FULL,???????????? // CSP類型 06.0))??????????????????????? // Flag values 07.{ 08.//以UserName為名的密鑰容器存在,那么我們已經得到了CSP的句柄 09.????printf("A crypto context with the %s key container \n", UserName); 10.????printf("has been acquired.\n\n"); 11.} 12.else //如果密鑰容器不存在,我們需要創建這個密鑰容器 13.{? 14.???if(CryptAcquireContext( 15.??????&hCryptProv,? 16.??????UserName,? 17.??????NULL,? 18.??????PROV_RSA_FULL,? 19.??????CRYPT_NEWKEYSET)) //創建以UserName為名的密鑰容器 20.???{ 21.????//創建密鑰容器成功,并得到CSP句柄 22.??????printf("A new key container has been created.\n"); 23.???} 24.???else 25.???{ 26.??????HandleError("Could not create a new key container.\n"); 27.????} 28.} // End of else好了,我們已經創建了密鑰容器,并得到了CSP的句柄。也可以這樣理解,我們得到了一個CSP的句柄,并且它被綁定到以UserName為名的密鑰容器上。嘿嘿……
那么,以后的加解密等操作,都將在這個CSP上進行。
可以如下刪除密鑰容器。
CryptAcquireContext(&hCryptProv, userName, NULL, PROV_RSA_FULL, CRYPT_DELETEKEYSET);
五、 一個文件加密的例子
看到這里肯定有人開始說了,“這么多廢話,還不快講怎么加密怎么解密!”您先別急,有些原理性的東西還是先了解了比較好,對以后的使用會有很大幫助。
言歸正傳,我們來看一段文件加密的代碼。
view source print? 001.#include < stdio.h > 002.#include < windows.h > 003.#include < wincrypt.h > 004.#define MY_ENCODING_TYPE? (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING) 005.#define KEYLENGTH? 0x00800000 006.void HandleError(char *s); 007.?? 008.//-------------------------------------------------------------------- 009.//? These additional #define statements are required. 010.#define ENCRYPT_ALGORITHM CALG_RC4? 011.#define ENCRYPT_BLOCK_SIZE 8? 012.?? 013.//?? Declare the function EncryptFile. The function definition 014.//?? follows main. 015.?? 016.BOOL EncryptFile( 017.?????????????????PCHAR szSource,? 018.?????????????????PCHAR szDestination,? 019.?????????????????PCHAR szPassword);? 020.?? 021.//-------------------------------------------------------------------- 022.//?? Begin main. 023.?? 024.void main(void)? 025.{? 026.????CHAR szSource[100];? 027.????CHAR szDestination[100];? 028.????CHAR szPassword[100];? 029.?????? 030.?????? 031.????printf("Encrypt a file. \n\n"); 032.????printf("Enter the name of the file to be encrypted: "); 033.????scanf("%s",szSource); 034.????printf("Enter the name of the output file: "); 035.????scanf("%s",szDestination); 036.????printf("Enter the password:"); 037.????scanf("%s",szPassword); 038.?????? 039.????//-------------------------------------------------------------------- 040.????// Call EncryptFile to do the actual encryption. 041.?????? 042.????if(EncryptFile(szSource, szDestination, szPassword)) 043.????{ 044.????????printf("Encryption of the file %s was a success. \n", szSource); 045.????????printf("The encrypted data is in file %s.\n",szDestination); 046.????} 047.????else 048.????{ 049.????????HandleError("Error encrypting file!");? 050.????}? 051.} // End of main 052.?? 053.//-------------------------------------------------------------------- 054.//?? Code for the function EncryptFile called by main. 055.?? 056.static BOOL EncryptFile( 057.????????????????????????PCHAR szSource,? 058.????????????????????????PCHAR szDestination,? 059.????????????????????????PCHAR szPassword) 060.????????????????????????//-------------------------------------------------------------------- 061.????????????????????????//?? Parameters passed are: 062.????????????????????????//???? szSource, the name of the input, a plaintext file. 063.????????????????????????//???? szDestination, the name of the output, an encrypted file to be? 064.????????????????????????//???????? created. 065.????????????????????????//???? szPassword, the password. 066.{? 067.????//-------------------------------------------------------------------- 068.????//?? Declare and initialize local variables. 069.?????? 070.????FILE *hSource;? 071.????FILE *hDestination;? 072.?????? 073.????HCRYPTPROV hCryptProv;? 074.????HCRYPTKEY hKey;? 075.????HCRYPTHASH hHash;? 076.?????????? 077.????PBYTE pbBuffer;? 078.????DWORD dwBlockLen;? 079.????DWORD dwBufferLen;? 080.????DWORD dwCount;? 081.?????? 082.????//-------------------------------------------------------------------- 083.????// Open source file.? 084.????if(hSource = fopen(szSource,"rb")) 085.????{ 086.????????printf("The source plaintext file, %s, is open. \n", szSource); 087.????} 088.????else 089.????{? 090.????????HandleError("Error opening source plaintext file!"); 091.????}? 092.?? 093.????//-------------------------------------------------------------------- 094.????// Open destination file.? 095.????if(hDestination = fopen(szDestination,"wb")) 096.????{ 097.????????printf("Destination file %s is open. \n", szDestination); 098.????} 099.????else 100.????{ 101.????????HandleError("Error opening destination ciphertext file!");? 102.????} 103.?? 104.????//以下獲得一個CSP句柄 105.????if(CryptAcquireContext( 106.????????&hCryptProv,? 107.????????NULL,?????????????? //NULL表示使用默認密鑰容器,默認密鑰容器名 108.//為用戶登陸名 109.????????NULL,? 110.????????PROV_RSA_FULL,? 111.????????0)) 112.????{ 113.????????printf("A cryptographic provider has been acquired. \n"); 114.????} 115.????else 116.????{ 117.????????if(CryptAcquireContext( 118.????????????&hCryptProv,? 119.????????????NULL,? 120.????????????NULL,? 121.????????????PROV_RSA_FULL,? 122.????????????CRYPT_NEWKEYSET))//創建密鑰容器 123.????????{ 124.????????????//創建密鑰容器成功,并得到CSP句柄 125.????????????printf("A new key container has been created.\n"); 126.????????} 127.????????else 128.????????{ 129.????????????HandleError("Could not create a new key container.\n"); 130.????????} 131.?????????? 132.????} 133.?? 134.????//-------------------------------------------------------------------- 135.????// 創建一個會話密鑰(session key) 136.????// 會話密鑰也叫對稱密鑰,用于對稱加密算法。 137.????// (注: 一個Session是指從調用函數CryptAcquireContext到調用函數 138.????//?? CryptReleaseContext 期間的階段。會話密鑰只能存在于一個會話過程) 139.?? 140.????//-------------------------------------------------------------------- 141.????// Create a hash object.? 142.????if(CryptCreateHash( 143.????????hCryptProv,? 144.????????CALG_MD5,? 145.????????0,? 146.????????0,? 147.????????&hHash)) 148.????{ 149.????????printf("A hash object has been created. \n"); 150.????} 151.????else 152.????{? 153.????????HandleError("Error during CryptCreateHash!\n"); 154.????}?? 155.?? 156.????//-------------------------------------------------------------------- 157.????// 用輸入的密碼產生一個散列 158.????if(CryptHashData( 159.????????hHash,? 160.????????(BYTE *)szPassword,? 161.????????strlen(szPassword),? 162.????????0)) 163.????{ 164.????????printf("The password has been added to the hash. \n"); 165.????} 166.????else 167.????{ 168.????????HandleError("Error during CryptHashData. \n");? 169.????} 170.?? 171.????//-------------------------------------------------------------------- 172.????// 通過散列生成會話密鑰 173.????if(CryptDeriveKey( 174.????????hCryptProv,? 175.????????ENCRYPT_ALGORITHM,? 176.????????hHash,? 177.????????KEYLENGTH,? 178.????????&hKey)) 179.????{ 180.????????printf("An encryption key is derived from the password hash. \n");? 181.????} 182.????else 183.????{ 184.????????HandleError("Error during CryptDeriveKey!\n");? 185.????} 186.????//-------------------------------------------------------------------- 187.????// Destroy the hash object.? 188.?????? 189.????CryptDestroyHash(hHash);? 190.????hHash = NULL;? 191.?????? 192.????//-------------------------------------------------------------------- 193.????//? The session key is now ready.? 194.?????? 195.????//-------------------------------------------------------------------- 196.????// 因為加密算法是按ENCRYPT_BLOCK_SIZE 大小的塊加密的,所以被加密的 197.// 數據長度必須是ENCRYPT_BLOCK_SIZE 的整數倍。下面計算一次加密的 198.// 數據長度。 199.?? 200.????dwBlockLen = 1000 - 1000 % ENCRYPT_BLOCK_SIZE;? 201.?????? 202.????//-------------------------------------------------------------------- 203.????// Determine the block size. If a block cipher is used,? 204.????// it must have room for an extra block.? 205.?????? 206.????if(ENCRYPT_BLOCK_SIZE > 1)? 207.????????dwBufferLen = dwBlockLen + ENCRYPT_BLOCK_SIZE;? 208.????else? 209.????????dwBufferLen = dwBlockLen;? 210.?????? 211.????//-------------------------------------------------------------------- 212.????// Allocate memory.? 213.????if(pbBuffer = (BYTE *)malloc(dwBufferLen)) 214.????{ 215.????????printf("Memory has been allocated for the buffer. \n"); 216.????} 217.????else 218.????{? 219.????????HandleError("Out of memory. \n");? 220.????} 221.????//-------------------------------------------------------------------- 222.????// In a do loop, encrypt the source file and write to the source file.? 223.?????? 224.????do? 225.????{? 226.?????????? 227.????????//-------------------------------------------------------------------- 228.????????// Read up to dwBlockLen bytes from the source file.? 229.????????dwCount = fread(pbBuffer, 1, dwBlockLen, hSource);? 230.????????if(ferror(hSource)) 231.????????{? 232.????????????HandleError("Error reading plaintext!\n"); 233.????????} 234.?????????? 235.????????//-------------------------------------------------------------------- 236.????????// 加密數據 237.????????if(!CryptEncrypt( 238.????????????hKey,?????????? //密鑰 239.????????????0,????????????? //如果數據同時進行散列和加密,這里傳入一個 240.//散列對象 241.????????????feof(hSource),? //如果是最后一個被加密的塊,輸入TRUE.如果不是輸. 242.????????????????????????????//入FALSE這里通過判斷是否到文件尾來決定是否為 243.//最后一塊。 244.????????????0,????????????? //保留 245.????????????pbBuffer,?????? //輸入被加密數據,輸出加密后的數據 246.????????????&dwCount,?????? //輸入被加密數據實際長度,輸出加密后數據長度 247.????????????dwBufferLen))?? //pbBuffer的大小。 248.????????{? 249.????????????HandleError("Error during CryptEncrypt. \n");? 250.????????}? 251.?????????? 252.????????//-------------------------------------------------------------------- 253.????????// Write data to the destination file.? 254.?????????? 255.????????fwrite(pbBuffer, 1, dwCount, hDestination);? 256.????????if(ferror(hDestination)) 257.????????{? 258.????????????HandleError("Error writing ciphertext."); 259.????????} 260.?????????? 261.????}? 262.????while(!feof(hSource));? 263.????//-------------------------------------------------------------------- 264.????//? End the do loop when the last block of the source file has been 265.????//? read, encrypted, and written to the destination file. 266.?????? 267.????//-------------------------------------------------------------------- 268.????// Close files. 269.?????? 270.????if(hSource)? 271.????????fclose(hSource);? 272.????if(hDestination)? 273.????????fclose(hDestination);? 274.?????? 275.????//-------------------------------------------------------------------- 276.????// Free memory.? 277.?????? 278.????if(pbBuffer)? 279.????????free(pbBuffer);? 280.?????? 281.????//-------------------------------------------------------------------- 282.????// Destroy session key.? 283.?????? 284.????if(hKey)? 285.????????CryptDestroyKey(hKey);? 286.?????? 287.????//-------------------------------------------------------------------- 288.????// Destroy hash object.? 289.?????? 290.????if(hHash)? 291.????????CryptDestroyHash(hHash);? 292.?????? 293.????//-------------------------------------------------------------------- 294.????// Release provider handle.? 295.?????? 296.????if(hCryptProv)? 297.????????CryptReleaseContext(hCryptProv, 0); 298.????return(TRUE);? 299.} // End of Encryptfile 300.?? 301.//-------------------------------------------------------------------- 302.//? This example uses the function HandleError, a simple error 303.//? handling function, to print an error message to the standard error? 304.//? (stderr) file and exit the program.? 305.//? For most applications, replace this function with one? 306.//? that does more extensive error reporting. 307.?? 308.void HandleError(char *s) 309.{ 310.????fprintf(stderr,"An error occurred in running the program. \n"); 311.????fprintf(stderr,"%s\n",s); 312.????fprintf(stderr, "Error number %x.\n", GetLastError()); 313.????fprintf(stderr, "Program terminating. \n"); 314.????exit(1); 315.} // End of HandleError上面的代碼來自MSDN,并作了修改。注釋已經很詳細了,這里就不贅述了,解密與加密大同小異,大家可以自己看代碼。
這次先寫這么多,也許很多人覺得我寫這些大家都知道,并且也太簡單了。不要急慢慢來,嘿嘿:)接下來會有一些比較深入和實用的技術。
參考:
MSDN相關章節。
(注:如果代碼編譯不過,加入宏定義:_WIN32_WINNT=0x0400)
?
?
總結
以上是生活随笔為你收集整理的Microsoft CryptoAPI加密技术(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Microsoft CryptoAPI加
- 下一篇: openssl与cryptoAPI交互A