在Windows平台下使用openssl调用CSP

本文介绍一种方法使得通过RSA_private_decrypt函数可以使用CSP中的私钥来进行解密和通过RSA_sign函数可以使用CSP中的私钥来进行签名。
     在openssl中RSA结构既可以表示公钥也可以表示私钥,它有一个类型为RSA_METHOD的域meth,该域包含一组实现RSA运算的函数指针。为了可以调用CSP,我们只需要实现一个特殊的RSA私钥,它包含有自己的RSA_METHOD结构。由于很多CSP中通常不都能获取明文的私钥,所以,我们实现的RSA私钥结构中只能包含有公钥信息,即n和e,然后根据公钥寻找到匹配的私钥句柄来进行相关的操作。具体来说,我们实现了一个函数:RSA *RSAToCAPIPrivKey(RSA *rsa)。该函数的参数是个RSA公钥,返回的是一个能够调用CSP私钥的RSA私钥。
     具体的实现如下:
1、从个人证书库中寻找存在私钥且和给定公钥相匹配的证书,如果不存在,则返回NULL
2、用RSA_new()创建一个新的RSA结构,把公钥的n和e复制过去
3、设置新的RSA结构的flags为RSA_FLAG_SIGN_VER,并为之设置一个可以调用CSP的RSA_METHOD结构
4、返回该RSA结构。
     实现的关键是RSA_METHOD结构。
     RSA_METHOD结构的定义如下:
struct rsa_meth_st
     {
     const char *name;
     int (*rsa_pub_enc)(int flen,const unsigned char *from,
                    unsigned char *to,
                    RSA *rsa,int padding);
     int (*rsa_pub_dec)(int flen,const unsigned char *from,
                    unsigned char *to,
                    RSA *rsa,int padding);
     int (*rsa_priv_enc)(int flen,const unsigned char *from,
                    unsigned char *to,
                    RSA *rsa,int padding);
     int (*rsa_priv_dec)(int flen,const unsigned char *from,
                    unsigned char *to,
                    RSA *rsa,int padding);
     int (*rsa_mod_exp)(BIGNUM *r0,const BIGNUM *I,RSA *rsa,BN_CTX *ctx);
     int (*bn_mod_exp)(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
                  const BIGNUM *m, BN_CTX *ctx,BN_MONT_CTX *m_ctx);
     int (*init)(RSA *rsa);            
     int (*finish)(RSA *rsa);      
     int flags;                  
     char *app_data;            
     int (*rsa_sign)(int type,
           const unsigned char *m, unsigned int m_length,
           unsigned char *sigret, unsigned int *siglen, const RSA *rsa);
     int (*rsa_verify)(int dtype,
           const unsigned char *m, unsigned int m_length,
           unsigned char *sigbuf, unsigned int siglen, const RSA *rsa);
     int (*rsa_keygen)(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb);
};
typedef struct rsa_meth_st RSA_METHOD;
     name--是RSA_METHOD的名称
     rsa_pub_enc--是RSA公钥加密的实现,RSA_public_encrypt就是由它实现的。我们不需要实现它。
     rsa_pub_dec--是RSA公钥解密的实现,RSA_public_decrypt就是由它实现的。我们也不需要实现它。如果RSA结构的flags域没有设置了RSA_FLAG_SIGN_VER或者没有实现rsa_verify,则RSA_verify也会使用它来验证签名。
     rsa_priv_enc--是RSA私钥加密的实现,RSA_private_encrypt就是由它实现的。我不知道怎么实现它。如果RSA结构的flags域没有设置了RSA_FLAG_SIGN_VER或者没有实现rsa_sign,则RSA_sign也会使用它来进行签名。
     rsa_priv_dec--是RSA私钥解密的实现,RSA_private_decrypt就是由它实现的。这是我们要实现的。
     rsa_mod_exp和bn_mod_exp是用来辅佐实现RSA算法的某些大整数运算的实现,具体我不清楚。我们也不需要实现它。
     init--初始化函数,RSA_new会调用它。我们也不需要实现它。
     finish--清理函数,RSA_free会调用它。我们也不需要实现它。
     flags--某些标志
     app_data--某些相关数据。我们也不需要它。
     rsa_sign--如果RSA结构的flags域设置了RSA_FLAG_SIGN_VER(也就是rsa->flags & RSA_FLAG_SIGN_VER为真)且rsa_sign不为NULL的时候,RSA_sign会使用它来进行签名。这是我们要实现的。
     rsa_verify--如果RSA结构的flags域设置了RSA_FLAG_SIGN_VER(也就是rsa->flags & RSA_FLAG_SIGN_VER为真)且rsa_verify不为NULL的时候,RSA_verify会使用它来验证签名。我们也不需要实现它。
     rsa_keygen--产生RSA密钥对的实现。如果不为NULL,RSA_generate_key_ex会使用它,否则使用内置的RSA密钥对的实现。我们也不需要实现它。
     
     rsa_priv_dec的实现如下:
1、从个人证书库中寻找存在私钥且和给定公钥相匹配的证书,并获取其CERT_KEY_PROV_INFO_PROP_ID属性的值
2、根据获取到的CSP信息,获得私钥句柄
3、把要解密的数据的格式从big-endian转换为little-endian。openssl使用big-endian格式,而CryptoAPI通常要求little-endian格式。
4、调用CryptDecrypt解密即可。

     rsa_sign的实现如下:
1、从个人证书库中寻找存在私钥且和给定公钥相匹配的证书,并获取其CERT_KEY_PROV_INFO_PROP_ID属性的值
2、根据获取到的CSP信息,获得容器句柄
3、使用容器句柄创建一个对应的hash对象,并设定hash值。
4、使用CryptSignHash签名
5、把签名得到的数据从little-endian转换为big-endian。
     
     该程序使用openssl-0.9.8在windows2000和XP下测试通过。
     存在的问题:
1、只能在windows2000、XP下使用,不能在98、Me中使用,原因是使用了CryptAcquireContextW函数。其实很容易把得到的CERT_KEY_PROV_INFO_PROP_ID属性的值的各宽字符串转换为多字节字符串,使用CryptAcquireContextA函数就可以在在98和Me使用了。
2、获取私钥句柄只是搜索个人证书库,可能会有些CSP的私钥无法使用。
3、没有使用openssl的错误报告函数给出详细的错误信息
4、解密的时候存在很多限制,这些限制和CryptDecrypt的一样。有些CSP根本不支持CryptDecrypt使用RSA解密,有的只有Algid为AT_KEYEXCHANGE才能进行解密。

 

OpenVPN已经实现了可以看它的这个函数SSL_CTX_use_CryptoAPI_certificate

  1. #include "openssl/rsa.h"
  2. #include "openssl/obj_mac.h"
  3. #include <windows.h>
  4. #include <string.h>
  5. static int capi_rsa_priv_dec(int flen,const unsigned char *from,unsigned char *to,RSA *rsa,int padding);
  6. static int capi_rsa_sign(int type,const unsigned char *m, unsigned int m_length,unsigned char *sigret, unsigned int *siglen, const RSA *rsa);
  7. static RSA *CAPI_RSA_new();
  8. static PCRYPT_KEY_PROV_INFO GetProvInfo(const RSA *rsa);
  9. static RSA_METHOD capirsameth=
  10. {
  11.     "Windows Crypto API RSA Method"/* name */
  12.     0,  /* rsa_pub_enc */
  13.     0,  /* rsa_pub_dec */
  14.     0, /* rsa_priv_enc */
  15.     capi_rsa_priv_dec, /* rsa_priv_dec */
  16.     0, /* rsa_mod_exp */
  17.     0, /* bn_mod_exp */
  18.     0, /* init */
  19.     0, /* finish */
  20.     RSA_FLAG_SIGN_VER, /* flags */
  21.     0, /* app_data */
  22.     capi_rsa_sign, /* rsa_sign */
  23.     0, /* rsa_verify */
  24.     0, /* rsa_keygen */
  25. };
  26. int capi_rsa_priv_dec(int flen,const unsigned char *from,unsigned char *to,RSA *rsa,int padding)
  27. {
  28.     int number;
  29.     PCRYPT_KEY_PROV_INFO keyinfo;
  30.     HCRYPTPROV hProv;
  31.     BOOL b;
  32.     DWORD dwKeySpec;
  33.     HCRYPTKEY hUserKey;
  34.     BYTE *data;
  35.     DWORD datalen;
  36.     int i;
  37.     if(flen<0||from==0||to==0||rsa==0||rsa->n==0||rsa->e==0)
  38.     {
  39.         return -1;
  40.     }
  41.     
  42.     number=RSA_size(rsa);
  43.     if(flen!=number)
  44.     {
  45.         return -1;
  46.     }
  47.     
  48.     switch(padding)
  49.     {
  50.     case RSA_PKCS1_PADDING:
  51.         break;
  52.     default:
  53.         return -1;
  54.     }
  55.     keyinfo=GetProvInfo(rsa);
  56.     if(keyinfo==0)
  57.     {
  58.         return -1;
  59.     }
  60.     
  61.     b=CryptAcquireContextW(&hProv,keyinfo->pwszContainerName,keyinfo->pwszProvName,keyinfo->dwProvType,keyinfo->dwFlags);
  62.     dwKeySpec=keyinfo->dwKeySpec;
  63.     free(keyinfo);
  64.     if(b==0)
  65.     {
  66.         return -1;
  67.     }
  68.     b=CryptGetUserKey(hProv,dwKeySpec,&hUserKey);
  69.     if(b==0)
  70.     {
  71.         CryptReleaseContext(hProv,0);
  72.         return -1;
  73.     }
  74.     datalen=flen;
  75.     data=(BYTE *)malloc(datalen);
  76.     if(data==0)
  77.     {
  78.         CryptDestroyKey(hUserKey);
  79.         CryptReleaseContext(hProv,0);
  80.         return -1;
  81.     }
  82.     for(i=0;i<flen;i++)
  83.     {
  84.         data[i]=from[flen-i-1];
  85.     }
  86.     b=CryptDecrypt(hUserKey,0,TRUE,0,data,&datalen);
  87.     CryptDestroyKey(hUserKey);
  88.     CryptReleaseContext(hProv,0);
  89.     if(b==0)
  90.     {
  91.         free(data);
  92.         return -1;
  93.     }
  94.     memcpy(to,data,datalen);
  95.     memset(data,0,datalen);
  96.     free(data);
  97.     return (int)datalen;
  98. }
  99. int capi_rsa_sign(int type,const unsigned char *m, unsigned int m_length,unsigned char *sigret, unsigned int *siglen, const RSA *rsa)
  100. {
  101.     int number;
  102.     PCRYPT_KEY_PROV_INFO keyinfo;
  103.     HCRYPTPROV hProv;
  104.     BOOL b;
  105.     DWORD dwKeySpec;
  106.     ALG_ID hashid;
  107.     HCRYPTHASH hHash;
  108.     unsigned int i;
  109.     unsigned char temp;
  110.     if(m_length<0||m==0||sigret==0||siglen==0||rsa==0||rsa->n==0||rsa->e==0)
  111.     {
  112.         return 0;
  113.     }
  114.     
  115.     number=RSA_size(rsa);
  116.     if((int)m_length>number)
  117.     {
  118.         return 0;
  119.     }
  120.     *siglen=number;
  121.     switch(type)
  122.     {
  123.     case NID_md5_sha1:
  124.         hashid=CALG_SSL3_SHAMD5;
  125.         if(m_length!=36)
  126.         {
  127.             return 0;
  128.         }
  129.         break;
  130.     case NID_md5:
  131.         hashid=CALG_MD5;
  132.         if(m_length!=16)
  133.         {
  134.             return 0;
  135.         }
  136.         break;
  137.     case NID_md2:
  138.         hashid=CALG_MD2;
  139.         if(m_length!=16)
  140.         {
  141.             return 0;
  142.         }
  143.         break;
  144.     case NID_sha1:
  145.         hashid=CALG_SHA1;
  146.         if(m_length!=20)
  147.         {
  148.             return 0;
  149.         }
  150.         break;
  151.     default :
  152.         return 0;
  153.     }
  154.     keyinfo=GetProvInfo(rsa);
  155.     if(keyinfo==0)
  156.     {
  157.         return 0;
  158.     }
  159.     
  160.     b=CryptAcquireContextW(&hProv,keyinfo->pwszContainerName,keyinfo->pwszProvName,keyinfo->dwProvType,keyinfo->dwFlags);
  161.     dwKeySpec=keyinfo->dwKeySpec;
  162.     free(keyinfo);
  163.     if(b==0)
  164.     {
  165.         return 0;
  166.     }
  167.     b=CryptCreateHash(hProv,hashid,0,0,&hHash);
  168.     if(b==0)
  169.     {
  170.         CryptReleaseContext(hProv,0);
  171.         return 0;
  172.     }
  173.     b=CryptSetHashParam(hHash,HP_HASHVAL,m,0);
  174.     if(b==0)
  175.     {
  176.         CryptDestroyHash(hHash);
  177.         CryptReleaseContext(hProv,0);
  178.         return 0;
  179.     }
  180.     b=CryptSignHash(hHash,dwKeySpec,0,0,sigret,(DWORD *)siglen);
  181.     CryptDestroyHash(hHash);
  182.     CryptReleaseContext(hProv,0);
  183.     if(b==0)
  184.     {
  185.         return 0;
  186.     }
  187.     for(i=0;i<(*siglen)/2;i++)
  188.     {
  189.         temp=sigret[i];
  190.         sigret[i]=sigret[*siglen-i-1];
  191.         sigret[*siglen-i-1]=temp;
  192.     }
  193.     return 1;
  194. }
  195. RSA *CAPI_RSA_new()
  196. {
  197.     RSA *rsa;
  198.     rsa=RSA_new();
  199.     if(rsa==0)
  200.     {
  201.         return 0;
  202.     }
  203.     RSA_set_method(rsa,&capirsameth);
  204.     rsa->flags=RSA_FLAG_SIGN_VER;
  205.     return rsa;
  206. }
  207. RSA *RSAToCAPIPrivKey(RSA *rsa)
  208. {
  209.     RSA *ret;
  210.     PCRYPT_KEY_PROV_INFO keyinfo;
  211.     HCRYPTPROV hProv;
  212.     BOOL b;
  213.     if(rsa==0||rsa->e==0||rsa->n==0)
  214.     {
  215.         return 0;
  216.     }
  217.     keyinfo=GetProvInfo(rsa);
  218.     if(keyinfo==0)
  219.     {
  220.         return 0;
  221.     }
  222.     b=CryptAcquireContextW(&hProv,keyinfo->pwszContainerName,keyinfo->pwszProvName,keyinfo->dwProvType,keyinfo->dwFlags);
  223.     free(keyinfo);
  224.     if(b==0)
  225.     {
  226.         return 0;
  227.     }
  228.     CryptReleaseContext(hProv,0);
  229.     ret=CAPI_RSA_new();
  230.     if(ret==0)
  231.     {
  232.         return 0;
  233.     }
  234.     ret->e=BN_dup(rsa->e);
  235.     if(ret->e==0)
  236.     {
  237.         RSA_free(ret);
  238.         return 0;
  239.     }
  240.     
  241.     ret->n=BN_dup(rsa->n);
  242.     if(ret->n==0)
  243.     {
  244.         RSA_free(ret);
  245.         return 0;
  246.     }
  247.     return ret;
  248.     
  249. }
  250. PCRYPT_KEY_PROV_INFO GetProvInfo(const RSA *rsa)
  251. {
  252.     unsigned char *rsapubkeybuf=0;
  253.     int rsapubkeybuflen;
  254.     BOOL b;
  255.     PCCERT_CONTEXT ctx1;
  256.     PCCERT_CONTEXT ctx2;
  257.     HCERTSTORE hStore;
  258.     PCRYPT_KEY_PROV_INFO data;
  259.     DWORD datalen;
  260.     
  261.     rsapubkeybuflen=i2d_RSAPublicKey(rsa,&rsapubkeybuf);
  262.     if(rsapubkeybuflen<=0)
  263.     {
  264.         return 0;
  265.     }
  266.     
  267.     hStore=CertOpenSystemStore(0,"MY");
  268.     if(hStore==0)
  269.     {
  270.         OPENSSL_free(rsapubkeybuf);
  271.         return 0;
  272.     }
  273.     
  274.     ctx2=0;
  275.     while((ctx1=CertEnumCertificatesInStore(hStore,ctx2)))
  276.     {
  277.         b=CertGetCertificateContextProperty(ctx1,CERT_KEY_PROV_INFO_PROP_ID, 0, &datalen);
  278.         if(b)
  279.         {
  280.             data=(PCRYPT_KEY_PROV_INFO)malloc(datalen);
  281.             if(data==0)
  282.             {
  283.                 OPENSSL_free(rsapubkeybuf);
  284.                 CertFreeCertificateContext(ctx1);
  285.                 CertCloseStore(hStore,0);
  286.                 return 0;
  287.             }
  288.             b=CertGetCertificateContextProperty(ctx1,CERT_KEY_PROV_INFO_PROP_ID, data, &datalen);
  289.             if(b)
  290.             {
  291.                 if(ctx1->pCertInfo->SubjectPublicKeyInfo.PublicKey.cUnusedBits==0&
  292.                     (int)ctx1->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData==rsapubkeybuflen &
  293.                     memcmp(ctx1->pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData,rsapubkeybuf,rsapubkeybuflen)==0)
  294.                 {
  295.                     OPENSSL_free(rsapubkeybuf);
  296.                     CertFreeCertificateContext(ctx1);
  297.                     CertCloseStore(hStore,0);
  298.                     return data;
  299.                 }
  300.                 else
  301.                 {
  302.                     free(data); 
  303.                 }
  304.             }
  305.             else
  306.             {
  307.                 free(data);
  308.             }
  309.         }
  310.         ctx2=ctx1;
  311.     }
  312.     OPENSSL_free(rsapubkeybuf);
  313.     CertCloseStore(hStore,0);
  314.     return 0;
  315. }

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值