|
程序中在web页面上生成证书请求的方法有两种,分别通过调用enumProviders接口和OpenSSL接口来实现。本文为您详细介绍这两种方法并提供相应的实例源代码。
一、在web页面上生成证书请求
在本地生成的证书请求的方式适用于CA不备份私钥或者说该私钥用于签名的情况。
1、枚举CSP
生成私钥的时候是通过枚举本地的CSP进行的,这个功能只需要通过调用xenroll.dll的enumProviders接口来实现。举例如下:
EnumProviders function EnumProviders() nCSPIndex=0 ON ERROR resume next do sProviderName=IControl.enumProviders(nCSPIndex, 0) if err.Number<>0 then If &H80070103=Err.Number Then Err.Clear exit do elseif err.number = 438 then document.location="xenroll.dll" exit do else msgbox Err.description exit do end if end if /*在这里添加你自己对CSP的显示部分*/ loop end function |
2、另外几个接口
xenroll.dll与生成证书请求相关的另外几接口是:
KeySpec ---用于设置或取得密钥的类型(即签名AT_SIGNATURE或密钥交换AT_KEYEXCHANGE) HashAlgorithm-- 用于设置或取得对证书进行散列的算法,可以为MD2/MD5/SHA1,默认是SHA1。 GenKeyFlags---- 用于控制生成的私钥是否可以导出。默认是不可导出的,但可以设置成CRYPT_EXPORTABLE。或者按里的定义#define CRYPT_EXPORTABLE 0x00000001直接设置成1也可以。 ProviderName-- 就是CSP的名字,就是从注册表时读的名字。 |
3、生成DN
自己可以做一个简单的网页用来接收输入信息,我们将用这些信息生成dn.如果有对DN不熟悉的可以看一下网站里pki/pmi版面的内容。
比如:我们可以用vbscript定义一个dn的变量,然后将它们拼接起来。
DN = ""; DN = DN + "C=" + "\"" + cCountry + "\"" + ","; DN = DN + "S=" + "\"" + cState + "\"" + ","; DN = DN + "L=" + "\"" + cCity + "\"" + ","; DN = DN + "CN=" + "\"" + cName + "\"" + ","; DN = DN + "E=" + "\"" + cEMail + "\""; |
4、调用CreatePKCS10接口
这个接口有两个参数第一个是上面所说的DN,另外一个是证书的用途,这里就一长串的OID,具体的用途可以看一下x.509 v3的rfx文档。
举例如下:
| szPKCS10 = IControl.CreatePKCS10(DN, "1.3.6.1.5.5.7.3.2"); |
二、调用openssl接口生成请求
调用OpenSSL接口生成证书相对复杂一点,不过也可通过下面几个步骤来完成。
1、生成RSA密钥对
第一个参数是密钥的长度,第二个是RSA算法中的e,这个参数在pkcs#1中有建议,
这里取65535(细节请到安全标准栏目中下载相关文档)
示例代码如下:
EVP_PKEY *pkey = NULL; RSA *rsa = NULL; rsa = RSA_generate_key(*(pReq->keylen),0x10001,NULL,NULL); pkey = EVP_PKEY_new(); if(!EVP_PKEY_assign_RSA(pkey,rsa)) { m_str.Format("%s","generate RSA key pair error"); goto err; } |
2、生成X509Name
在这里我用了一个类来处理
// 加一个条目 int CX509Name::AddEntry(int key, char *value) {
char *key_value[]={"countryName","stateOrProvinceName","localityName", "organizationName","organizationalUnitName","commonName", "emailAddress"}; int nid; X509_NAME_ENTRY *ent; if(subject == NULL)return -1; if ((nid = OBJ_txt2nid(key_value[key])) == NID_undef) return -1; if (!(ent = X509_NAME_ENTRY_create_by_NID(NULL, nid,MBSTRING_ASC,(unsigned char*)value, -1))) return -1; if (X509_NAME_add_entry(subject, ent, -1, 0) != 1) return -1; return 1; } // 取得最终的结果 X509_NAME* CX509Name::getName(REQ_INFO *p) { if(p->szCN) AddEntry(0,p->szCN); if(p->szST) AddEntry(0,p->szST); if(p->szL) AddEntry(0,p->szL); if(p->szO) AddEntry(0,p->szO); if(p->szOU) AddEntry(0,p->szOU); if(p->szName) AddEntry(0,p->szName); if(p->szEmail) AddEntry(0,p->szEmail); return subject; } |
3、设置好其它信息并签名
除了上面所的信息要设置以外,还要设置版本号、公钥、名字等。
req=X509_REQ_new(); if(!req) { goto err; } subject=x_name.getName(pReq); if(!subject) { goto err; } if(!X509_REQ_set_version(req,0L)) goto err; if(!X509_REQ_set_subject_name(req,subject)) goto err; if(!X509_REQ_set_pubkey(req,pkey)) goto err; if(!X509_REQ_sign(req,pkey,digest)) goto err; |
4、保存
// output private key outPvk = BIO_new_file(szPrivateKeyFileName, "w"); if (!outPvk) { m_str.Format("Error opening file %s", szPrivateKeyFileName); goto err; } if (pkey) { if(szPrivateKeyPwd) { int len =strlen(szPrivateKeyPwd); PEM_write_bio_PrivateKey(outPvk, pkey, EVP_des_ede3_cbc(), (unsigned char*)szPrivateKeyPwd,len, NULL, NULL); } else PEM_write_bio_PrivateKey(outPvk, pkey, NULL,NULL,0, NULL, NULL); } // output certificate request file outReq = BIO_new_file(szReqFileName, "w"); if (!outReq) { m_str.Format("Error opening file %s", szReqFileName); goto err; } if (req) { PEM_write_bio_X509_REQ(outReq, req); } |
以上代码中是片段,仅供参考,本人能力有限,欢迎交流与指正,谢谢。
|