使用 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
