作者: 討飯貓
簡介 這篇文章將教你如何使用 Visual C++ 和 ATL 工具和 Microsoft CryptoAPI 建立一個能加/解密數(shù)據(jù)的組件。 Crypto 101 本文使用Microsoft® Cryptographic Application Programming Interface (CryptoAPI),將苦澀難懂的邏輯算法操作隱藏起來,如果想知道詳細信息請參看[url href=http://premium.microsoft.com/msdn/library/]MSDN Library[/url].如果想知道更多的密碼系統(tǒng),我推薦你看看這本書 [url href=http://www.aspzone.com/books/bookRedir.asp?url=http://www.amazon.com/exec/obidos/ASIN/0471117099/aspzonecom]Bruce Schneier's Applied Cryptography: Protocols, Algorithms, and Source Code in C[/url]
建立組件 首先,用"ATL COM AppWizard”建立一個新project。在這個例子中,我將其命名為”CryptoProj”。在 server type中選擇”Dynamic Link Library (DLL)”,點按”Finish”繼續(xù)。
定義界面 在 insert 菜單中點按"New ATL Object...",選擇 “Simple Object”,然后按 Next。
在 “Names” 欄中,設 short name 為 “Crypto”,其他項照下面的填寫。
在 Attributes 欄,確定 Apartment Threading Model 被選上,Support IsupportErrorInfo 選項勾上,然后按 OK.
按右鍵點擊 Icrypto ,點”Add Method”加一個方法.
將該方法取名為”Encrypt”,在參數(shù)欄輸入"[in] BSTR bstrPlainText, [in] BSTR bstrPassword, [out, retval] VARIANT *vCipherText".
再加一個方法,取名為”Decrypt”,參數(shù)為"[in] VARIANT vCipherText, [in] BSTR bstrPassword, [out, retval] BSTR *bstrPlainText"
實現(xiàn)加密方法 需要包括 CryptoAPI 庫,在 Crypto.cpp 頭加一句: #include <wincrypt.h>
現(xiàn)在來定義我們需要的各種變量。 STDMETHODIMP CCrypto::Encrypt(BSTR bstrPlainText, BSTR bstrPassword, VARIANT *vCipherText) { BYTE *pbData; BYTE *pbPassword; SAFEARRAY* psa; HCRYPTPROV hProv = 0; HCRYPTHASH hHash = 0; HCRYPTKEY hKey = 0; DWORD dwCryptDataLen = 0; DWORD dwDataLen = 0; DWORD dwError = 0; char buffer[200];
USES_CONVERSION;
由于許多CryptoAPI 調用要用注冊表,所以需要執(zhí)行一句RevertToSelf(). RevertToSelf();
下一步,我們需要將輸入變量轉化為我們能用的格式。 dwDataLen = SysStringLen(bstrPlainText); pbData = (BYTE*)OLE2A(bstrPlainText); pbPassword = (BYTE*)OLE2A(bstrPassword);
然后,用CryptAcquireContext function取得省缺 Crypto provider的句柄。 // Get handle to the default provider. if (! CryptAcquireContext(&hProv, "aspZoneCryptoComponent\0", MS_DEF_PROV, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET)) { if (! CryptAcquireContext(&hProv, "aspZoneCryptoComponent\0", MS_DEF_PROV, PROV_RSA_FULL, (CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))) { dwError = GetLastError(); sprintf(buffer, "Error %x during CryptAcquireContext", dwError); return Error(buffer); } }
我們通過創(chuàng)建一個 one-way-hash密碼得到session key。 // Create a hash object. if ( ! CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash)) { dwError = GetLastError(); sprintf(buffer, "Error %x during CryptCreateHash", dwError); return Error(buffer); }
// Hash in the password. if ( ! CryptHashData(hHash, pbPassword, SysStringLen(bstrPassword), 0)) { dwError = GetLastError(); sprintf(buffer, "Error %x during CryptHashData", dwError); return Error(buffer); }
// Derive a session key from the hash object. if ( ! CryptDeriveKey(hProv, ENCRYPT_ALGORITHM, hHash, 0, &hKey)) { dwError = GetLastError(); sprintf(buffer, "Error %x during CryptDeriveKey", dwError); return Error(buffer); }
// Destroy hash object. CryptDestroyHash(hHash); hHash = 0;
現(xiàn)在來加密我們的數(shù)據(jù)。 // Encrypt the Data. dwCryptDataLen = dwDataLen; if ( ! CryptEncrypt(hKey, 0, true, 0, pbData, &dwCryptDataLen, dwDataLen)) { dwError = GetLastError(); sprintf(buffer, "Error %x during CryptEncrypt", dwError); return Error(buffer); }
我們將加密后的數(shù)據(jù)放入一個數(shù)組中,而不是一個string里,因為它可能會包含null。雖然 BSTR 能處理null的情況,但不能保證用戶調用環(huán)境能正確處理,所以一個數(shù)組是最好的選擇。 // Place Encrypted Data into a VARIANT SAFEARRAY of VARIANT BYTE SAFEARRAYBOUND rgsabound[] = {dwCryptDataLen, 0}; psa = SafeArrayCreate(VT_VARIANT, 1, rgsabound); VARIANT* rgElems; SafeArrayAccessData(psa, (LPVOID*)&rgElems); for(DWORD i=0;i<dwCryptDataLen;i++){ VariantInit(&rgElems[i]); rgElems[i].vt = VT_UI1; rgElems[i].uiVal = pbData[i]; } SafeArrayUnaccessData(psa); VariantInit(vCipherText); vCipherText->vt = (VT_ARRAY | VT_VARIANT) ; vCipherText->parray = psa;
稍微整理一下,搞定。 // Destroy session key. if(hKey) CryptDestroyKey(hKey);
// Release provider handle. if(hProv) CryptReleaseContext(hProv, 0);
return S_OK;
實現(xiàn)解密方法 首先定義變量。 STDMETHODIMP CCrypto::Decrypt(VARIANT vCipherText, BSTR bstrPassword, BSTR *bstrPlainText) { HCRYPTPROV hProv = 0; HCRYPTHASH hHash = 0; HCRYPTKEY hKey = 0; SAFEARRAY* psa; VARIANT HUGEP *pVar; BYTE *pbData; BYTE *pbPassword; long lBound, uBound; DWORD dwCryptDataLen = 0; DWORD dwOffset = 0; DWORD dwError = 0; char buffer[200];
USES_CONVERSION;
同樣的原因,我們要調用RevertToSelf() RevertToSelf();
現(xiàn)在,當接收一個數(shù)組參數(shù)作為變量,該數(shù)組可能藏在結構中的某個地方,所以需要一個判斷嵌套。 //Get the safe array out of the Variant. if (vCipherText.vt == (VT_VARIANT | VT_BYREF)) { if (vCipherText.pvarVal->vt == (VT_ARRAY | VT_VARIANT)) SafeArrayCopy(vCipherText.pvarVal->parray, &psa); else { if (vCipherText.pvarVal->vt == (VT_ARRAY | VT_VARIANT | VT_BYREF)) SafeArrayCopy(*(vCipherText.pvarVal->pparray), &psa); } } else { if (vCipherText.vt == (VT_ARRAY | VT_VARIANT | VT_BYREF)) SafeArrayCopy(*(vCipherText.pparray), &psa); else { if (vCipherText.vt == (VT_ARRAY | VT_VARIANT)) SafeArrayCopy(vCipherText.parray, &psa); else return DISP_E_TYPEMISMATCH; } }
需要密文和密碼都是BYTE*類型。 //Convert the SAFEARRAY into a form we can use. SafeArrayAccessData(psa, (void HUGEP* FAR*)&pVar); SafeArrayGetLBound(psa, 1, &lBound); SafeArrayGetUBound(psa, 1, &uBound);
dwOffset = 0 - lBound; dwCryptDataLen = uBound + dwOffset + 1;
//Allocate memory pbData = (BYTE *)malloc(dwCryptDataLen);
//Copy the array for(DWORD i = lBound; i <= uBound; i++){ if( ! (pVar[i].vt & VT_UI1)){ //Data Elements must be VT_UI1 (Bytes). free(pbData); return DISP_E_TYPEMISMATCH; } pbData[i + dwOffset]="pVar[i].uiVal;" } //Get Password pbPassword="(BYTE*)OLE2A(bstrPassword);</PRE">
取得 Crypto Provider 的句柄。 // Get handle to the default provider. if (! CryptAcquireContext(&hProv, "aspZoneCryptoComponent\0", MS_DEF_PROV, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET)) { if (! CryptAcquireContext(&hProv, "aspZoneCryptoComponent\0", MS_DEF_PROV, PROV_RSA_FULL, (CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))) { dwError = GetLastError(); sprintf(buffer, "Error %x during CryptAcquireContext", dwError); return Error(buffer); } }
從 password 中得到 session key. // Create a hash object. if ( ! CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash)) { dwError = GetLastError(); sprintf(buffer, "Error %x during CryptCreateHash", dwError); return Error(buffer); }
// Hash in the password. if ( ! CryptHashData(hHash, pbPassword, SysStringLen(bstrPassword), 0)) { dwError = GetLastError(); sprintf(buffer, "Error %x during CryptHashData", dwError); return Error(buffer); }
// Derive a session key from the hash object. if ( ! CryptDeriveKey(hProv, ENCRYPT_ALGORITHM, hHash, 0, &hKey)) { dwError = GetLastError(); sprintf(buffer, "Error %x during CryptDeriveKey", dwError); return Error(buffer); }
// Destroy hash object. CryptDestroyHash(hHash); hHash = 0;
將密文解密到純文本中。 // Decrypt the Data. if ( ! CryptDecrypt(hKey, 0, true, 0, pbData, &dwCryptDataLen)) { dwError = GetLastError(); sprintf(buffer, "Error %x during CryptDecrypt", dwError); return Error(buffer); }
//Terminate the string with a null pbData[dwCryptDataLen] = NULL;
設置返回值,大掃除,然后搞定。 //Place Decrypted data into retval *bstrPlainText = SysAllocString(A2OLE((const char *)pbData));
// Destroy session key. if(hKey) CryptDestroyKey(hKey);
// Release provider handle. if(hProv) CryptReleaseContext(hProv, 0);
return S_OK; }
原著:討飯貓 來自:chinaasp.com
|