当前位置导航:炫浪网>>网络学院>>编程开发>>C++教程>>Visual C++教程

使用Visual C++创建Crypto加/解密组件

     简介
    这篇文章将教你如何使用 Visual C++ 和 ATL 工具和 Microsoft CryptoAPI 建立一个能加/解密数据的组件。
    Crypto 101
    本文使用Microsoft® Cryptographic Application Programming Interface (CryptoAPI),将苦涩难懂的逻辑算法操作隐藏起来,如果想知道详细信息请参看[url href=http://premium.microsoft.com/msdn/library/]MSDN Library[/url].如果想知道更多的密码系统,我推荐你看看这本书 [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”继续。

    定义界面
    在 insert 菜单中点按"New ATL Object...",选择 “Simple Object”,然后按 Next。

    在 “Names” 栏中,设 short name 为 “Crypto”,其他项照下面的填写。

    在 Attributes 栏,确定 Apartment Threading Model 被选上,Support IsupportErrorInfo 选项勾上,然后按 OK.

    按右键点击 Icrypto ,点”Add Method”加一个方法.

    将该方法取名为”Encrypt”,在参数栏输入"[in] BSTR bstrPlainText, [in] BSTR bstrPassword, [out, retval] VARIANT *vCipherText".

    再加一个方法,取名为”Decrypt”,参数为"[in] VARIANT vCipherText, [in] BSTR bstrPassword, [out, retval] BSTR *bstrPlainText"

    实现加密方法
    需要包括 CryptoAPI 库,在 Crypto.cpp 头加一句: #include <wincrypt.h>


    现在来定义我们需要的各种变量。 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 调用要用注册表,所以需要执行一句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);
    }
    }


    我们通过创建一个 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;


    现在来加密我们的数据。 // 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);
    }


    我们将加密后的数据放入一个数组中,而不是一个string里,因为它可能会包含null。虽然 BSTR 能处理null的情况,但不能保证用户调用环境能正确处理,所以一个数组是最好的选择。 // 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;


    实现解密方法
    首先定义变量。 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();


    现在,当接收一个数组参数作为变量,该数组可能藏在结构中的某个地方,所以需要一个判断嵌套。 //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;
    }

相关内容
赞助商链接