已阅读5页,还剩7页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Openssl和PKCS#11的故事1.1目标通过Openssl和PKCS#11接口,使用USBKEY中的私钥和证书来签发一个下级证书。1.2背景数字证书颁发过程一般为:用户首先产生自己的密钥对,并将公共密钥及部分个人身份信息传送给认证中心。认证中心在核实身份后,将执行一些必要的步骤,以确信请求确实由用户发送而来,然后,认证中心将发给用户一个数字证书,该证书内包含用户的个人信息和他的公钥信息,同时还附有认证中心的签名信息。一个标准的X.509数字证书包含以下一些内容:证书的版本信息;证书的序列号,每个证书都有一个唯一的证书序列号;证书所使用的签名算法;证书的发行机构名称,命名规则一般采用X.500格式;证书的有效期,现在通用的证书一般采用UTC时间格式,它的计时范围为1950-2049;证书所有人的名称,命名规则一般采用X.500格式;证书所有人的公开密钥;证书发行者对证书的签名。简而言之,CA从PKCS#10证书请求(或者P7格式)中读取用户信息和公钥信息,使用这些信息封装成一个X.509格式(可能是不同版本,比较普遍是V3),此时唯一没有包括的是证书发行者对证书的签名,此时使用CA的私钥进行签名,得到签名值后CA将其填充到X.509相对应的结构中去,一个X.509证书宝宝就此诞生了。此处唯一不同的是CA的公私钥对和证书都存放在USBKEY中(当然也能存放在加密机或加密卡中),所以将通过USBKEY的PKCS#11接口完成上述操作,而证书相关操作就由Openssl代劳了。1.3正题第一、使用Usbkey向某个CA申请一个证书通过下面的命令来验证,第一组公私钥对和证书是签名证书,第二组是加密证书。可以很明显地看出他们是通过Csp方式操作整个证书申请过程的。C:Program FilesSmart card bundlepkcs11-tool.exe -module DMPKCS11.dll OCertificate Object, type = X.509 certlabel: cert addey by CSPID: 37af001ddbd525e640ca3c3f6d78b009741d1f48Public Key Object; RSA 1024 bitslabel: pub key addey by CSPID: 37af001ddbd525e640ca3c3f6d78b009741d1f48Usage: encrypt, verifyPrivate Key Object; RSAlabel: private key addey by CSPID: 37af001ddbd525e640ca3c3f6d78b009741d1f48Usage: decrypt, signCertificate Object, type = X.509 certlabel: cert addey by CSPID: ab268f4320a426b4a6ce70d757cd11fcd83b8dddPublic Key Object; RSA 1024 bitslabel: pub key addey by CSPID: ab268f4320a426b4a6ce70d757cd11fcd83b8dddUsage: encrypt, verifyPrivate Key Object; RSAlabel: private key addey by CSPID: ab268f4320a426b4a6ce70d757cd11fcd83b8dddUsage: decrypt, sign第二、生成PKCS#11的证书请求这里直接使用Java程序生成一个证书请求。import java.io.OutputStreamWriter;import java.security.KeyPair;import java.security.KeyPairGenerator;import javax.security.auth.x500.X500Principal;import org.bouncycastle.jce.PKCS10CertificationRequest;import org.bouncycastle.openssl.PEMWriter;/* Generation of a basic PKCS #10 request.*/public class PKCS10CertRequestExample public static PKCS10CertificationRequest generateRequest( KeyPair pair) throws Exception return new PKCS10CertificationRequest(SHA256withRSA, new X500Principal(C=CN,ST=上海,L=上海,O=火星,OU=北极,CN=超人), pair.getPublic(), null, pair.getPrivate(); public static void main( String args) throws Exception / create the keys KeyPairGenerator kpGen = KeyPairGenerator.getInstance(RSA, BC); kpGen.initialize(1024, Utils.createFixedRandom(); KeyPair pair = kpGen.generateKeyPair(); PKCS10CertificationRequest request = generateRequest(pair); PEMWriter pemWrt = new PEMWriter(new OutputStreamWriter(System.out); pemWrt.writeObject(request); pemWrt.close(); 证书请求-BEGIN CERTIFICATE REQUEST-MIIBoDCCAQkCAQAwYjEPMA0GA1UEAwwG6LaF5Lq6MQ8wDQYDVQQLDAbljJfmnoExDzANBgNVBAoMBueBq+aYnzEPMA0GA1UEBwwG5LiK5rW3MQ8wDQYDVQQIDAbkuIrmtbcxCzAJBgNVBAYTAkNOMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCw7iyU/8p1lCxnJifdqxNYO1cTVg35BBtscQsrtug9Br3Vge/kNX9KC5xOGhdcK1IDjl3d1CGsRtnb4dEFqtkjKWQ1z5WZxXWoVfkqwP3AJg8y10BhiiDqPPbn3II4o8Nc+bvztDm32HbNXcyXWLR5aEJx1FiJYdDmDbRbgGrcawIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAJSr2pe1LJp+gSWAc7yVufbnYXG3QgzIdoEUhP1I/3LNeqUYyuTaL/fTgAFoEjTvwOlAVizcve8qiD9/ApY+MtjgRKFDbZYnkC3mRgJTDxV3WzDmdj4YEQGIUVGO+XRfiWP132n9N3aI6gaJVj2m7Zu56akrE3F2c4kawZL/aIK-END CERTIFICATE REQUEST-第三、程序签发1. engine_pkcs11的使用方式 使用openssl调用USBKEY的PKCS#11接口,可以通过OpenSC项目的engine_pkcs11接口。原本使用编写openssl配置文件方式(见1),但是就是无法使用,两次调用ListEngine()都无法发现pkcs11 engine的影子。Openssl.conf 内容:openssl_conf = openssl_defopenssl_defengines = engine_sectionengine_sectionpkcs11 = pkcs11_sectionpkcs11_sectionengine_id = pkcs11dynamic_path = C:Program FilesSmart card bundleengine_pkcs11.dllMODULE_PATH = C:WindowsSystem32DMPKCS11.dllinit = 0reqdistinguished_name = req_distinguished_namereq_distinguished_name可以通过下面命令验证配置文件并没有写错,openssl识别出了pkcs11 engine,并且生成了证书请求。C:Program FilesSmart card bundleopenssl req -config openssl.conf -engine pkcs11 -new -key id_37af001ddbd525e640ca3c3f6d78b009741d1f48 -keyform engine -out req.pem -text -x509 -subj /CN=Andreas Jellinghausengine pkcs11 set.PKCS#11 token PIN:所以最后还是使用动态调用的方式导入pkcs11 engine,即ENGINE_load_dynamic。所以两次调用ListEngine()后发现, dynamic engine导入pkcs11 engine后就会被其替换。导入前id: dynamic, name: Dynamic engine loading support导入后id: pkcs11, name: pkcs11 engine2. 导出USBKEY中的CA证书需要导出CA证书,这是因为CA需要填充X.509格式中的证书的发行机构名称。通过” LOAD_CERT_CTRL”命令来获取证书,输入的参数为证书的表示。slot_0-id_37af001ddbd525e640ca3c3f6d78b009741d1f48slot_0 PKCS#11 表示的第一个插槽(一个插槽配一个Token)id_37af001ddbd525e640ca3c3f6d78b009741d1f48 证书的Id号(同一组公私钥对和证书这个ID是相同的),这个ID可以通过pkcs11-tools获得。命令返回的parms.cert就指向一个X.509结构的证书。但是必须要注意的是导出证书前,必须设置过正确的PIN struct const char * cert_id; X509 * cert; parms; parms.cert_id = slot_0-id_37af001ddbd525e640ca3c3f6d78b009741d1f48; parms.cert = NULL; ENGINE_ctrl_cmd(e, LOAD_CERT_CTRL, 0, &parms, NULL, 1); 3. 证书请求通过JAVA生成的证书请求,直接复制粘贴到工程目录下的文本文件certreq.txt即可,并且需要包含BEGIN/END部分。4. 从证书请求中获取用户信息 /设置证书的主体名称,req就是刚刚生成的请求证书 X509_set_subject_name(m_pClientCert, X509_REQ_get_subject_name(req); /设置证书的公钥信息 X509_set_pubkey(m_pClientCert, X509_PUBKEY_get(req-req_info-pubkey); 5. 设置证书的签发者信息 /设置证书的签发者信息,m_pCACert是CA证书 X509_set_issuer_name(m_pClientCert, X509_get_subject_name(m_pCACert);6. 证书签名注意这里采用的是sha1的摘要算法,当然也可使用MD5 /设置签名值 / EVP_sha1 是否可以设置成别的,如EVP_md5 / 这样一份X509证书就生成了,下面的任务就是对它进行编码保存。 X509_sign(m_pClientCert, m_pCAKey, EVP_sha1(); 此处还有些补充的内容,为了验证X509_sign调用PKCS#11接口的情况,自己实现了一个PKCS#11的包装壳(68个导出函数),实现时注意C_GetFunctionList应该指向本包装壳的函数,不然错误的使用实际的C_GetFunctionList作返回结构便也就失去意义了。X509_sign的调用方式还是不同的,java中如果使用SHA1WithRSA传入到PKCS#11接口的C_Sign或者C_SignUpdate的数据是完整的明文,但是X509_sign传入的是一个ASN.1 Sequence的一个结构,结构中包含待签名数据的摘要散列。举例来说:待加密的数据是Hello World! ,在C_Sign传入的数据中就可以发现Hello World!的SHA-1的摘要散列。待加密:Hello World!SHA-1: 2EF7BDE608CE5404E97D5F042F95F89F1C232871C_Sign: 30 21 30 09 06 05 2b 0e 03 02 1a 05 00 04 14 2e f7 bd e6 08 ce 54 04 e9 7d 5f 04 2f 95 f8 9f 1c 23 28 71使用ASN.1dump来观察就看的更加清楚了。从X509_sign的实现也可以看的这一点,在RSA_Sign之前首先进行摘要算法,并且这个摘要并不使用PKCS#11中的接口函数,直接使用Openssl自己的摘要算法,所以传入到最后的只是明文摘要散列了。int X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md)/先进行ret-cert_info-signature,以及ret-sig_alg的设置;inl=i2d_X509_CINF(ret-cert_info,NULL);/求出证书编码后的长度buf_in=(unsigned char *)OPENSSL_malloc(unsigned int)inl);/申请空间outll=outl=EVP_PKEY_size(pkey1);buf_outl=(unsigned char *)OPENSSL_malloc(unsigned int)inl);if (buf_in = NULL) (buf_outl= NULL) outl=0; goto err; p=buf_in;/p与buf-in共享一段地址 i2d_X509_CINF(ret-cert_info,&p);/将证书编码存入buf-in EVP_MD_CTX_init(&ctxl);/初始化 EVP_SignInit(&ctxl,dgst);/将需要使用的摘要算法存入ctxl中 EVP_SignUpdate(&ctxl,(unsigned char *)buf_in,inl);/存入证书的编码值 EVP_DigestFinal(&ctxl,&(m0),&m_len);/求取编码的长度为m_len摘要值存入m中 RSA_sign(ctxl-digest-type,m,m_len,buf_out,outl,pkey-pkey.rsa)/求取摘要值的签名值,最后将长度为outl的签名值存入buf-out。 .7. 最后生成的证书USBKEY 中包含证书是向三级CA申请的,所以处于第四级,使用第四级证书来签发新证书,” 超人”宝宝就只能到第五级去了(也许是第五项修炼吧)。其实还是个问题,第四级证书报“此证书似乎对于所选的目的是有效。”,出现此问题的原因嵌入在消息中的签名证书链包含一个无效的交叉引用,估计第四级是一个用户证书,要消除这个感叹号,第四级证书的证书用法中应该包含Digital Signature, Certificate Signing, Off-line CRL Signing, CRL Signing (86)这几项8完整代码/ SignWithOpenSSL.cpp : 定义控制台应用程序的入口点。/#include stdafx.h#include #include #include #include #include #include #include #include #include #include #define OPENSSL_LOAD_CONF #define UC_ENGINE_SOPATH C:Program FilesSmart card bundleengine_pkcs11.dll#define UC_EXPECTED_ENGINE_ID pkcs11#define UC_ENGINE_MODULEPATH C:WindowsSystem32DMPKCS11.dll/ 列出当前所有的engine/*ENGINE *ENGINE_get_first(void);ENGINE *ENGINE_get_last(void);ENGINE *ENGINE_get_next(ENGINE *e);ENGINE *ENGINE_get_prev(ENGINE *e);*/void ListEngine() ENGINE *current; current = ENGINE_get_first(); if( NULL != current ) printf(id: %s, name: %sn, ENGINE_get_id(current), ENGINE_get_name(current); while( NULL != (current = ENGINE_get_next(current) printf(id: %s, name: %sn, ENGINE_get_id(current), ENGINE_get_name(current); /*存储证书*/int save_cert(X509 *pCert, char *pCertFile) BIO *pbio; if(NULL = pCert | NULL = pCertFile) return -1; pbio = BIO_new_file(pCertFile, w); if(NULL = pbio) return -1; if(!i2d_X509_bio(pbio, pCert) printf(save_cert:call PEM_write_bio_X509 error ); return -1; printf(Bingo, New Cert is bornedn); BIO_free(pbio); return 0;void add_subject_entity(X509_NAME *pSubjectName, char *key, char *value) int nid; X509_NAME_ENTRY *ent; if( (nid =OBJ_txt2nid(key) = NID_undef ) printf( add_subject_entity:concert nid error); return ; ent = X509_NAME_ENTRY_create_by_NID( NULL, nid, MBSTRING_UTF8, (unsigned char*)value, -1); if(ent = NULL) printf(add_subject_entity:create ent error); return; if(X509_NAME_add_entry(pSubjectName, ent, -1, 0) != 1) printf(add_subject_entity:add to subjectname error); return; return;int CreateX509Cert(X509 *m_pCACert, EVP_PKEY *m_pCAKey) / 读取证书请求 BIO *in; X509_REQ *req=NULL,*req2=NULL; in = BIO_new_file(certreq.txt,r); req = PEM_read_bio_X509_REQ(in,NULL,NULL,NULL); if( req = NULL ) printf(DER Decode Error!n); else printf(DER Decode Success!n); / 使用usbkey中的私钥进行签名 X509 *m_pClientCert; m_pClientCert = X509_new(); /设置版本号 X509_set_version(m_pClientCert, 2); /设置证书序列号,这个sn就是CA中心颁发的第N份证书 ASN1_INTEGER_set(X509_get_serialNumber(m_pClientCert),100); /设置证书开始时间 X509_gmtime_adj(X509_get_notBefore(m_pClientCert),0); /设置证书结束时间 X509_gmtime_adj(X509_get_notAfter(m_pClientCert), (long)60*60*24); /设置证书的主体名称,req就是刚刚生成的请求证书 X509_set_subject_name(m_pClientCert, X509_REQ_get_subject_name(req); /设置证书的公钥信息 X509_set_pubkey(m_pClientCert, X509_PUBKEY_get(req-req_info-pubkey); /设置证书的签发者信息,m_pCACert是CA证书 X509_set_issuer_name(m_pClientCert, X509_get_subject_name(m_pCACert); /设置扩展项目 X509V3_CTX ctx; X509V3_set_ctx(&ctx, m_pCACert, m_pClientCert, NULL, NULL, 0); X509_EXTENSION *x509_ext = X509_EXTENSION_new(); x509_ext = X509V3_EXT_conf(NULL, &ctx, HELLO, HELLO); X509_add_ext(m_pClientCert,x509_ext,-1); /设置签名值 / EVP_sha1 是否可以设置成别的,如EVP_md5 / 这样一份X509证书就生成了,下面的任务就是对它进行编码保存。 X509_sign(m_pClientCert, m_pCAKey, EVP_sha1(); / 输出证书 save_cert(m_pClientCert, d:test.cer); return 0;int main(int argc, CHAR* argv) ENGINE *e; const char *engine_id = pkcs11; const char *key_id = 37af001ddbd525e640ca3c3f6d78b009741d1f48; UI_METHOD *ui_method = NULL; EVP_PKEY *priv_key; void *cb_data; const char *config_name = NULL; BIO *bio_err=NULL; /* Load the config file */ /OPENSSL_config(config_name); / 不使用Openssl0.9.8e的配置文件来导入PKCS11 ENGINE_load_dynamic(); ListEngine(); printf(nLoading Dynamic.n); /* Register engine */ printf(Registering enginen); e = ENGINE_by_id(dynamic); if(!e) /* the engine isnt available */ printf(The engine isnt availablen); return 0; /int ENGINE_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f)(void); ENGINE_ctrl(e, ENGINE_CTRL_SET_LOGSTREAM, 0, bio_err, 0); / 设置engine_pkcs11的路径 ENGINE_ctrl_cmd_string(e, SO_PATH, UC_ENGINE_SOPATH, 0); ENGINE_ctrl_cmd_string(e, ID, UC_EXPECTED_ENGINE_ID, 0); ENGINE_ctrl_c
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 超市商品订购合同
- 北京市物业区域管理合同
- FDA进口预申报委托协议简单版样式
- 11声音是什么(原卷版)
- 四川省棠湖中学高三下学期周练数学(文)试题46
- 北京人朝初一分班考数学试题及答案
- 第5课 中国古代官员的选拔与管理 课件 高二历史统编版2019选择性必修1 国家制度与社会治理
- 广东省惠州市高三4月模拟考试文综地理试题
- 陕西省石泉县江南高级中学高中历史必修二人民版14古代中国的经济政策(下)教案
- 工程数学积分变换答案
- 2024至2030年全球与中国充电桩运营平台市场现状及未来发展趋势
- 2024-2025学年七年级生物上册 第二单元第一、二章 单元测试卷(人教版)
- 2024年高考地理真题完全解读(甘肃卷)
- 两弹一星精神(教学设计)-2023-2024学年小学科学课后服务科普课程
- 部编人教版六年级上册道德与法治全册知识点考点+典型考题【每课】
- 2024义务教育艺术新课标课程标准2022版考试题库及答案
- 2024-2030年中国免烧砖行业市场发展分析及前景趋势与投资研究报告
- 战舰波将金号拉片笔记
- DL∕T 1614-2016 电力应急指挥通信车技术规范
- 2024年云南大理州州级机关统一公开遴选公务员16名(高频重点提升专题训练)共500题附带答案详解
- 湖南长沙青竹湖2025届化学九年级第一学期期中达标检测试题含解析
评论
0/150
提交评论