关于使用CryptoApi实现非对称加密

本文详细介绍了作者在学习非对称加密过程中遇到的问题及解决方案,包括密钥生成、加密解密流程,并通过实例展示了如何实现对长文本的分组加密。文章深入浅出,适合初学者参考。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为了这个为对称加密的程序忙活了一阵子了。

开始的时候对PKI的加密的原理都不懂,而我又不想那么盲目的在网上找类似的程序,所以闷头看了一些资料,了解了加密的常识。但是开始编程序的时候并不那么顺利,开始的时候对非对称的加密了解不够深刻,甚至把会话加密也归类到了非对称加密。所以第一个写的程序是会话加密的程序。后来加密好内容发送的时候才发现,根本不是那么一回事。
 
关键的问题在Key的生成这里:
BOOL WINAPI CryptGenKey(
  HCRYPTPROV hProv,
  ALG_ID Algid,
  DWORD dwFlags,
  HCRYPTKEY* phKey
)


注意CryptGenKey的参数:
hProv
A handle to a cryptographic service provider (CSP) created by a call to CryptAcquireContext.
Algid
An ALG_ID value that identifies the algorithm for which the key is to be generated. Values for this parameter vary depending on the CSP used.

For a Diffie-Hellman CSP, use one of the following values.

ValueMeaning
CALG_DH_EPHEMSpecifies an "Ephemeral" Diffie-Hellman key.
CALG_DH_SFSpecifies a "Store and Forward" Diffie-Hellman key.

In addition to generating session keys for symmetric algorithms, this function can also generate public/private key pairs. Each CryptoAPI client generally possesses two public/private key pairs. To generate one of these key pairs, set the Algid parameter to one of the following values.

ValueMeaning
AT_KEYEXCHANGEKey exchange
AT_SIGNATUREDigital signature

CryptGetKeyParam (when the KP_ALGID parameter is specified) depend on the provider used. To determine which algorithm identifier is used by the different providers for the key specs AT_KEYEXCHANGE and AT_SIGNATURE, see ALG_ID.

dwFlags

Specifies the type of key generated. The sizes of a session key,RSA signature key, and RSA key exchange keys can be set when the key is generated.

  phKey
[out] Address to which the function copies the handle of the newly generated key. When you have finished using the key, delete the handle to the key by calling the CryptDestroyKey function.

上面这些都是msdn上的一些内容,产生非对称加密key对的话,只要把第二个参数输入AT_KEYEXCHANGE就可以了。

然后加密,导出key,都没有太大的悬念,但是有个问题困扰过我一段时间。就是关于CSP的非对称加密长度的问题。默认是117字节。但是我一直以为可以修改某个参数而实现对长文本的加密。但是最终也没有找到一个方法。最后我只好采用了分组加密的方法。关键方法如下:  

加密:

/**********************************************************************
+Function    name: CRYPTAPI_RSAEncrypt(unsigned char *pbSrcData, int nSrcLen, unsigned char *pbDestData, int &nDestLen)
+Input  parameter: 'pbSrcData' Encrypting data
       'nSrcLen' Length of encrypting data
       'pbDestData' Encrypted data
       'nDestLen' Length of encrypted data
+output parameter: 'pbDestData' Encrypted data
       'nDestLen' Length of encrypted data
+Return:
+Remark:           Encrypt data by private key
***********************************************************************/

int CKeyOperation::CRYPTAPI_RSAEncrypt(unsigned char *pbSrcData, int nSrcLen,
            unsigned char *pbDestData, int &nDestLen,
            BYTE *pbPubKey, DWORD &dwPubKeyLen,
          BYTE *pbPriKey, DWORD &dwPriKeyLen)//nDestLen = (nSrcLen/117 +1)*128
{
 int r;
 
 CryptAcquireContext(&m_hProv, "ASYENCRYPT", MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_DELETEKEYSET);

 if(!CryptAcquireContext(&m_hProv, "ASYENCRYPT", MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_NEWKEYSET))
  return -1;
 
 //Gennerate  key pair 
 if(!CryptGenKey(m_hProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, &m_hKey))
 {
  //Delete container
  CryptAcquireContext(&m_hProv, "ASYENCRYPT", MS_ENHANCED_PROV,
        PROV_RSA_FULL, CRYPT_DELETEKEYSET);
  CryptReleaseContext(m_hProv, 0);
  return -2;
 }
 
 //unsigned char *tempDestData = new unsigned char[nDestLen];
 unsigned char *tempDivSrcData = new unsigned char[128+1];
 int j=0;
 int t=0;
 //r = CryptEncrypt(m_hKey, 0, TRUE, 0, pbSrcData, (unsigned long *)&nSrcLen, 2048);
 for(int i=0;i*117<nSrcLen;i++)
 { 
  memset(tempDivSrcData,0x00,128+1);
  for(j=0;j<117&&j<nSrcLen;j++)
  {
   tempDivSrcData[j]=pbSrcData[(i*117)+j];
  }
  r = CryptEncrypt(m_hKey, 0, TRUE, 0, tempDivSrcData, (unsigned long *)&j, 128);
  if(!r)
  {
   if(m_hProv)
    CryptReleaseContext(m_hProv, 0);
   if(m_hKey)
    CryptDestroyKey(m_hKey);
   return -2;
  }
  t += j;
  for(int m = 0;m<j;m++)
  {
   pbDestData[(i*j)+m] = tempDivSrcData[m];
   //memcpy(pbDestData,tempDivSrcData,
  }
 }

 

 nDestLen = t;
 //memcpy(pbDestData, pbSrcData, nDestLen);

 //Export privatekey
 DWORD dwTempPriLen;
 
 r = CryptExportKey(m_hKey, NULL, PRIVATEKEYBLOB, NULL, NULL, &dwTempPriLen);
 if(!r)
 {
  //Failed to export the length of privatekey
  if(m_hProv)
    CryptReleaseContext(m_hProv, 0);
  if(m_hKey)
    CryptDestroyKey(m_hKey);
  return -5;
 }
 
 BYTE *pbTempPriData = (BYTE *)malloc(dwTempPriLen+1);
 
 r = CryptExportKey(m_hKey, NULL, PRIVATEKEYBLOB, NULL, pbTempPriData, &dwTempPriLen);
 if(!r)
 {
  //Failed to export privatekey
  if(m_hProv)
    CryptReleaseContext(m_hProv, 0);
  if(m_hKey)
    CryptDestroyKey(m_hKey);
  return -6;
 }
 
 //Export public key
 DWORD dwTempPubLen;
 
 r = CryptExportKey(m_hKey, NULL, PUBLICKEYBLOB, NULL, NULL, &dwTempPubLen);
 if(!r)
 {
  //Failed to export the length of public key
  if(m_hProv)
    CryptReleaseContext(m_hProv, 0);
  if(m_hKey)
    CryptDestroyKey(m_hKey);
  return -7;
 }
 
 BYTE *pbTempPubData = (BYTE *)malloc(dwTempPubLen+1);
 
 r = CryptExportKey(m_hKey, NULL, PUBLICKEYBLOB, NULL, pbTempPubData, &dwTempPubLen);
 if(!r)
 {
  //Failed to export public key
  if(m_hProv)
   CryptReleaseContext(m_hProv, 0);
  if(m_hKey)
   CryptDestroyKey(m_hKey);
  return -8;
 }
 
 //Copy buffer
 memcpy(pbPriKey, pbTempPriData, dwTempPriLen); //Copy data of private key
 dwPubKeyLen = dwTempPubLen; //Length of private key
 memcpy(pbPubKey, pbTempPubData, dwTempPubLen); //Copy data of public key
 dwPriKeyLen = dwTempPriLen; //Length of public key
 
 //Delete container
 //CryptAcquireContext(&hProv, "RSA", MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_DELETEKEYSET);


 //Release memery
 if(pbTempPriData)
  free(pbTempPriData);
 if(pbTempPubData)
  free(pbTempPubData);
 if(m_hKey)
   CryptDestroyKey(m_hKey);
 if(m_hProv)
   CryptReleaseContext(m_hProv, 0);
  
 return 0;
}

 

解密:

/**********************************************************************
+Function    name: CRYPTAPI_RSADecrypt(unsigned char *pbSrcData, int nSrcLen, unsigned char *pbDestData, int &nDestLen,BYTE *pbPubKey, DWORD dwPubLen)
+Input  parameter: unsigned char *pbSrcData,
       int nSrcLen,
       unsigned char *pbDestData,
       int &nDestLen,
       BYTE *pbPubKey,
       DWORD dwPubLen
+output parameter: unsigned char *pbDestData,
       int &nDestLen,
+Return:
+Remark: Decrypt the data with a imported key         
***********************************************************************/
int CDecryptOperation::CRYPTAPI_RSADecrypt(unsigned char *pbSrcData, int nSrcLen,
             unsigned char *pbDestData, int &nDestLen,
              BYTE *pbPriKey, DWORD dwPriLen)
{
 HCRYPTPROV m_hProv;
 HCRYPTKEY m_hKey;

 CryptAcquireContext(&m_hProv,  "ASYENCRYPT", MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_DELETEKEYSET);
 if(!CryptAcquireContext(&m_hProv, "ASYENCRYPT", MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_NEWKEYSET))
  return -1;

 
 int r;
 // private key import
 r = CryptImportKey(m_hProv, pbPriKey, dwPriLen, NULL, NULL, &m_hKey);
 //Public key import
 //r = CryptImportKey(m_hProv, pbPubKey, dwPubLen, NULL, NULL, &m_hKey);
 if(!r)
 {
  return -4;
 }
 //int r = CryptDecrypt(m_hKey, 0, TRUE, 0, pbSrcData, (unsigned long *)&nSrcLen);
 unsigned char *tempDivSrcData = new unsigned char[128+1];
 int j=0;
 int t=0;
 for(int i=0;i*128<nSrcLen;i++)
 { 
  memset(tempDivSrcData,0x00,128+1);
  for(j=0;j<128&&j<nSrcLen;j++)
  {
   tempDivSrcData[j]=pbSrcData[(i*128)+j];
  }
  r = CryptDecrypt(m_hKey, 0, TRUE, 0, tempDivSrcData, (unsigned long *)&j);
  if(!r)
  {
   if(m_hProv)
    CryptReleaseContext(m_hProv, 0);
   if(m_hKey)
    CryptDestroyKey(m_hKey);
   return -2;
  }
  t += j;
  for(int m = 0;m<j;m++)
  {
   pbDestData[(i*j)+m] = tempDivSrcData[m];
  }
 }
 if(!r)
 {
  return -3;
 }

 nDestLen = t;

 return 0;
}

 

 

因为是初学c++,加密解密的内容又是第一次接触,程序完成的过程中参考了网上许多示例,但无法一一统计,向这些朋友的辛勤工作表示感谢。

当然,程序中许多不足的地方,比如内存泄漏(一听这个词我就心惊肉跳,就好像大冷天的,屋顶上扯开了道口子,因为时间紧,没时间看资料,我现在还不太清除该如何规范的写c++才能避免这些问题,因为我在c#使用的时候,很少过多的关心这些问题);看来以后要努力的学习了。

整理下自己的思路,也希望能给有类似需求的朋友一些启示。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值