天天看點

用CryptoAPI進行資料加密、解密

---- 因為過于複雜的加密算法實作起來非常困難,是以在過去,許多應用程式隻能使用非常簡單的加密技術,這樣做的結果就是加密的資料很容易就可以被人破譯。而使用Microsoft提供的加密應用程式接口(即Cryptography API),或稱CryptoAPI,就可以友善地在應用程式中加入強大的加密功能,而不必考慮基本的算法。本文将對CryptoAPI及其使用的資料加密原理作一簡單的介紹,然後給出了用CryptoAPI編寫加密程式的大緻步驟,最後以一個檔案的加密、解密程式為例示範了CryptoAPI的部分功能。

---- 1. CryptoAPI簡介

---- CryptoAPI是一組函數,為了完成數學計算,必須具有密碼服務提供者子產品(CSP)。Microsoft通過捆綁RSA Base Provider在作業系統級提供一個CSP,使用RSA公司的公鑰加密算法,更多的CSP可以根據需要增加到應用中。事實上,CSP有可能與特殊硬體裝置(如智能卡)一起來進行資料加密。CryptoAPI接口允許簡單的函數調用來加密資料,交換公鑰,散列一個消息來建立摘要以及生成數字簽名。它還提供進階的管理操作,如從一組可能的CSP中使用一個CSP。此外,CryptoAPI還為許多進階安全性服務提供了基礎,包括用于電子商務的SET,用于加密客戶機/伺服器消息的PCT,用于在各個平台之間來回傳遞機密資料和密鑰的PFX,代碼簽名等等。CryptoAPI的體系結構如下圖:

---- 目前支援CryptoAPI的Windows系統有:Windows 95 OSR2、Windows NT SP3及後續版本、Windows 98、Windows 2000等。CryptoAPI的配置資訊存儲在系統資料庫中,包括如下密鑰:

HKEY_LOCAL_MACHINE\SOFTWARE\

Microsoft \ Cryptography \Defaults

HKEY_CURRENT_USER\ Software \ Microsoft

 \ Cryptography \Providers

---- 2.資料加密原理

---- 資料加密的流程如下圖:

---- CryptoAPI使用兩種密鑰:會話密鑰與公共/私人密鑰對。會話密鑰使用相同的加密和解密密鑰,這種算法較快,但必須保證密鑰的安全傳遞。公共/私人密鑰對使用一個公共密鑰和一個私人密鑰,私人密鑰隻有專人才能使用,公共密鑰可以廣泛傳播。如果密鑰對中的一個用于加密,另一個一定用于解密。公共/私人密鑰對算法很慢,一般隻用于加密小批資料,例如用于加密會話密鑰。

---- CryptoAPI支援兩種基本的編碼方法:流式編碼和塊編碼。流式編碼在明碼文本的每一位上建立編碼位,速度較快,但安全性較低。塊編碼在一個完整的塊上(一般為64位)上工作,需要使用填充的方法對要編碼的資料進行舍入,以組成多個完整的塊。這種算法速度較慢,但更安全。

---- 3.應用舉例

---- 下面以兩個檔案加密與解密的C程式片斷為例,示範一下CryptoAPI的強大功能。這兩個程式均為Win32控制台應用,程式省略了出錯處理,實際運作時請加入。

---- ①檔案加密

#include < windows.h >

#include < stdio.h >

#include < stdlib.h >

#include < wincrypt.h >

//确定使用RC2塊編碼或是RC4流式編碼

#ifdef USE_BLOCK_CIPHER

#define ENCRYPT_ALGORITHMCALG_RC2

#define ENCRYPT_BLOCK_SIZE8

#else

   #define ENCRYPT_ALGORITHMCALG_RC4

   #define ENCRYPT_BLOCK_SIZE1

#endif

void CAPIDecryptFile(PCHAR szSource,

PCHAR szDestination, PCHAR szPassword);

void _cdecl main(int argc, char *argv[])

{

PCHAR szSource= NULL;

PCHAR szDestination = NULL;

PCHAR szPassword= NULL;

// 驗證參數個數

if(argc != 3 && argc != 4) {

printf("USAGE: decrypt < source file >

 < dest file > [ < password > ]\n");

exit(1);

}

//讀取參數.

szSource   = argv[1];

szDestination  = argv[2];

if(argc == 4) {

szPassword = argv[3];

CAPIDecryptFile(szSource, szDestination, szPassword);

/*szSource為要加密的檔案名稱,szDestination

為加密過的檔案名稱,szPassword為加密密碼*/

void CAPIEncryptFile(PCHAR szSource, PCHAR

szDestination, PCHAR szPassword)

FILE *hSource  = NULL;

FILE *hDestination = NULL;

INT eof = 0;

HCRYPTPROV hProv   = 0;

HCRYPTKEY hKey = 0;

HCRYPTKEY hXchgKey = 0;

HCRYPTHASH hHash   = 0;

PBYTE pbKeyBlob = NULL;

DWORD dwKeyBlobLen;

PBYTE pbBuffer = NULL;

DWORD dwBlockLen;

DWORD dwBufferLen;

DWORD dwCount;

hSource = fopen(szSource,"rb"));// 打開源檔案.

hDestination = fopen(szDestination,"wb") ;

//.打開目标檔案

   // 連接配接預設的CSP

CryptAcquireContext(&hProv, NULL, NULL,

PROV_RSA_FULL, 0));

if(szPassword == NULL) {

//密碼為空,使用随機産生的會話密鑰加密

// 産生随機會話密鑰.

CryptGenKey(hProv, ENCRYPT_ALGORITHM,

CRYPT_EXPORTABLE, &hKey)

// 取得密鑰交換對的公共密鑰

CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hXchgKey);

// 計算隐碼長度并配置設定緩沖區

CryptExportKey(hKey, hXchgKey, SIMPLEBLOB, 0,

NULL, &dwKeyBlobLen);

pbKeyBlob = malloc(dwKeyBlobLen)) == NULL) ;

// 将會話密鑰輸出至隐碼

CryptExportKey(hKey, hXchgKey, SIMPLEBLOB,

0, pbKeyBlob, &dwKeyBlobLen));

// 釋放密鑰交換對的句柄

CryptDestroyKey(hXchgKey);

hXchgKey = 0;

// 将隐碼長度寫入目标檔案

fwrite(&dwKeyBlobLen, sizeof(DWORD), 1, hDestination);

//将隐碼長度寫入目标檔案

fwrite(pbKeyBlob, 1, dwKeyBlobLen, hDestination);

} else {

//密碼不為空, 使用從密碼派生出的密鑰加密檔案

CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash);

// 建立散清單

CryptHashData(hHash, szPassword, strlen(szPassword), 0);

 //散列密碼

// 從散清單中派生密鑰

CryptDeriveKey(hProv, ENCRYPT_ALGORITHM, hHash, 0, &hKey);

// 删除散清單

CryptDestroyHash(hHash);

hHash = 0;

 }

 //計算一次加密的資料位元組數,必須為ENCRYPT_BLOCK_SIZE的整數倍

dwBlockLen = 1000 - 1000 % ENCRYPT_BLOCK_SIZE;

//如果使用塊編碼,則需要額外空間

if(ENCRYPT_BLOCK_SIZE  > 1) {

dwBufferLen = dwBlockLen + ENCRYPT_BLOCK_SIZE;

dwBufferLen = dwBlockLen;

//配置設定緩沖區

pbBuffer = malloc(dwBufferLen);

//加密源檔案并寫入目标檔案

do {

// 從源檔案中讀出dwBlockLen個位元組

dwCount = fread(pbBuffer, 1, dwBlockLen, hSource);

eof = feof(hSource);

//加密資料

CryptEncrypt(hKey, 0, eof, 0, pbBuffer,

&dwCount, dwBufferLen);

// 将加密過的資料寫入目标檔案

fwrite(pbBuffer, 1, dwCount, hDestination);

} while(!feof(hSource));

printf("OK\n");

……//關閉檔案、釋放記憶體

   }

②檔案解密

void CAPIDecryptFile(PCHAR szSource, PCHAR

……//變量聲明、檔案操作同檔案加密程式

CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0);

  if(szPassword == NULL) {

// 密碼為空,使用存儲在加密檔案中的會話密鑰解密

// 讀隐碼的長度并配置設定記憶體

fread(&dwKeyBlobLen, sizeof(DWORD), 1, hSource);

pbKeyBlob = malloc(dwKeyBlobLen)) == NULL);

// 從源檔案中讀隐碼.

fread(pbKeyBlob, 1, dwKeyBlobLen, hSource);

// 将隐碼輸入CSP

CryptImportKey(hProv, pbKeyBlob,

dwKeyBlobLen, 0, 0, &hKey);

// 密碼不為空, 使用從密碼派生出的密鑰解密檔案

CryptDeriveKey(hProv, ENCRYPT_ALGORITHM,

hHash, 0, &hKey);