帝国软件
  设为首页 加入收藏 关于我们
 
解密帝国网站管理系统
栏 目:
 
您的位置:首页 > 技术文档 > ASP编程
使用 Visual C++创建Crypto加/解密组件
作者:讨饭猫 发布时间:2005-03-12 来源:www.chinaasp.com
简介
这篇文章将教你如何使用 Visual C++ 和 ATL 工具和 Microsoft CryptoAPI 建立一个能加/解密数据的组件。
Crypto 101
本文使用Microsoft® Cryptographic Application Programming Interface (CryptoAPI),将苦涩难懂的逻辑算法操作隐藏起来,如果想知道详细信息请参看MSDN Library.如果想知道更多的密码系统,我推荐你看看这本书 Bruce Schneier's 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", MS_DEF_PROV,
PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
{
if (! CryptAcquireContext(&hProv,
"aspZoneCryptoComponent", 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",
MS_DEF_PROV, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
{
if (! CryptAcquireContext(&hProv, "aspZoneCryptoComponent",
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

  
评论】【加入收藏夹】【 】【打印】【关闭
※ 相关链接
 ·如何在VC++ 编写的组件中使用 AD  (2005-03-12)
 ·使用索引服务器 - 使用索引服务器  (2005-03-12)
 ·使用索引服务器 - 创建ASP页面  (2005-03-12)
 ·使用索引服务器 - 增加属性  (2005-03-12)
 ·如何使用asp创建dsn?  (2005-03-12)
 ·一种比使用include adovbs.inc更  (2005-03-12)
 ·正则表达式简介(微软)--3.使用  (2005-03-12)
 ·使用ASP组件的一点经验  (2005-03-12)
 ·如何使用XSL和正则表达式来验证数  (2005-03-12)
 ·如何使用XSL和正则表达式来验证数  (2005-03-12)

   栏目导行
  PHP编程
  ASP编程
  ASP.NET编程
  JAVA编程
   站点最新
·致合作伙伴的欢迎信
·媒体报道
·帝国软件合作伙伴计划协议
·DiscuzX2.5会员整合通行证发布
·帝国CMS 7.0版本功能建议收集
·帝国网站管理系统2012年授权购买说
·PHPWind8.7会员整合通行证发布
·[官方插件]帝国CMS-访问统计插件
·[官方插件]帝国CMS-sitemap插件
·[官方插件]帝国CMS内容页评论AJAX分
   类别最新
·在ASP中使用数据库
·使用ASP脚本技术
·通过启动脚本来感受ASP的力量
·学习使用ASP对象和组件
·解析asp的脚本语言
·初看ASP-针对初学者
·ASP开发10条经验总结
·ASP之对象总结
·ASP与数据库应用(给初学者)
·关于学习ASP和编程的28个观点
 
关于帝国 | 广告服务 | 联系我们 | 程序开发 | 网站地图 | 留言板 帝国网站管理系统