欢迎光临
我们一直在努力

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

建站超值云服务器,限时71元/月

 
使用 visual c++创建crypto/解密组件
简介
这篇文章将教你如何使用 visual c++ 和 atl 工具和 microsoft cryptoapi 建立一个能加/解密数据的组件。
crypto 101
本文使用microsoft® cryptographic application programming interface (cryptoapi),将苦涩难懂的逻辑算法操作隐藏起来,如果想知道详细信息请参看msdn library.如果想知道更多的密码系统,我推荐你看看这本书 bruce schneiers applied cryptography: protocols, algorithms, and source code in c

建立组件
首先,用"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;
}

翻译:讨饭猫
jan,10 2000
下载doc文档(带图):
http://64.13.189.254/cafecat/build a crypto component using visual c++ and atl .doc

赞(0)
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com 特别注意:本站所有转载文章言论不代表本站观点! 本站所提供的图片等素材,版权归原作者所有,如需使用,请与原作者联系。未经允许不得转载:IDC资讯中心 » 使用 Visual C++创建Crypto加/解密组件
分享到: 更多 (0)