简介
这篇文章将教你如何使用 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
