日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Openssl和PKCS#11的故事

發(fā)布時間:2025/3/20 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Openssl和PKCS#11的故事 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

原文:http://www.cnblogs.com/adylee/archive/2009/08/03/1537813.html

1.1?? What to do

通過Openssl和PKCS#11接口,使用USBKEY中的私鑰和證書來簽發(fā)一個下級證書。

1.2????????????? 背景

數(shù)字證書頒發(fā)過程一般為:用戶首先產(chǎn)生自己的密鑰對,并將公共密鑰及部分個人身份信息傳送給認(rèn)證中心。認(rèn)證中心在核實身份后,將執(zhí)行一些必要的步驟,以確信請求確實由用戶發(fā)送而來,然后,認(rèn)證中心將發(fā)給用戶一個數(shù)字證書,該證書內(nèi)包含用戶的個人信息和他的公鑰信息,同時還附有認(rèn)證中心的簽名信息。

?

?

?

?

一個標(biāo)準(zhǔn)的X.509數(shù)字證書包含以下一些內(nèi)容:

證書的版本信息;

證書的序列號,每個證書都有一個唯一的證書序列號;

證書所使用的簽名算法;

證書的發(fā)行機(jī)構(gòu)名稱,命名規(guī)則一般采用X.500格式;

證書的有效期,現(xiàn)在通用的證書一般采用UTC時間格式,它的計時范圍為1950-2049;

證書所有人的名稱,命名規(guī)則一般采用X.500格式;

證書所有人的公開密鑰;

證書發(fā)行者對證書的簽名。

?

簡而言之,CA從PKCS#10證書請求(或者P7格式)中讀取用戶信息和公鑰信息,使用這些信息封裝成一個X.509格式(可能是不同版本,比較普遍是V3),此時唯一沒有包括的是證書發(fā)行者對證書的簽名,此時使用CA的私鑰進(jìn)行簽名,得到簽名值后CA將其填充到X.509相對應(yīng)的結(jié)構(gòu)中去,一個X.509證書寶寶就此誕生了。

?

此處唯一不同的是CA的公私鑰對和證書都存放在USBKEY中(當(dāng)然也能存放在加密機(jī)或加密卡中),所以將通過USBKEY的PKCS#11接口完成上述操作,而證書相關(guān)操作就由Openssl代勞了。

?

1.3????????????? 正題

第一、使用Usbkey向某個CA申請一個證書

通過下面的命令來驗證,第一組公私鑰對和證書是簽名證書,第二組是加密證書。可以很明顯地看出他們是通過Csp方式操作整個證書申請過程的。

C:\Program Files\Smart card bundle>pkcs11-tool.exe --module DMPKCS11.dll –O

Certificate Object, type = X.509 cert

label:????? cert addey by CSP

ID:???????? 37af001ddbd525e640ca3c3f6d78b009741d1f48

Public Key Object; RSA 1024 bits

label:????? pub key addey by CSP

ID:???????? 37af001ddbd525e640ca3c3f6d78b009741d1f48

Usage:????? encrypt, verify

Private Key Object; RSA

label:????? private key addey by CSP

ID:???????? 37af001ddbd525e640ca3c3f6d78b009741d1f48

Usage:????? decrypt, sign

Certificate Object, type = X.509 cert

label:????? cert addey by CSP

ID:???????? ab268f4320a426b4a6ce70d757cd11fcd83b8ddd

Public Key Object; RSA 1024 bits

label:????? pub key addey by CSP

ID:???????? ab268f4320a426b4a6ce70d757cd11fcd83b8ddd

Usage:????? encrypt, verify

Private Key Object; RSA

label:????? private key addey by CSP

ID:???????? ab268f4320a426b4a6ce70d757cd11fcd83b8ddd

Usage:????? 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-----

MIIBoDCCAQkCAQAwYjEPMA0GA1UEAwwG6LaF5Lq6MQ8wDQYDVQQLDAbljJfmnoEx

DzANBgNVBAoMBueBq+aYnzEPMA0GA1UEBwwG5LiK5rW3MQ8wDQYDVQQIDAbkuIrm

tbcxCzAJBgNVBAYTAkNOMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCw7iyU

/8p1lCxnJifdqxNYO1cTVg35BBtscQsrtug9Br3Vge/kNX9KC5xOGhdcK1IDjl3d

1CGsRtnb4dEFqtkjKWQ1z5WZxXWoVfkqwP3AJg8y10BhiiDqPPbn3II4o8Nc+bvz

tDm32HbNXcyXWLR5aEJx1FiJYdDmDbRbgGrcawIDAQABMA0GCSqGSIb3DQEBCwUA

A4GBAJSr2pe1LJp++gSWAc7yVufbnYXG3QgzIdoEUhP1I/3LNeqUYyuTaL/fTgAF

oEjTvwOlAVizcve8qiD9/ApY+MtjgRKFDbZYnkC3mRgJTDxV3WzDmdj4YEQGIUVG

O+XRfiWP132n9N3aI6gaJVj2m7Zu56akrE3F2c4kawZL/aIK

-----END CERTIFICATE REQUEST-----

?

第三、程序簽發(fā)

1. engine_pkcs11的使用方式

?????? 使用openssl調(diào)用USBKEY的PKCS#11接口,可以通過OpenSC項目的engine_pkcs11接口。原本使用編寫openssl配置文件方式(見[1]),但是就是無法使用,兩次調(diào)用ListEngine()都無法發(fā)現(xiàn)pkcs11 engine的影子。

Openssl.conf 內(nèi)容:

?

openssl_conf = openssl_def

?

[openssl_def]

engines = engine_section

?

[engine_section]

pkcs11 = pkcs11_section

?

[pkcs11_section]

engine_id = pkcs11

dynamic_path = "C:\\Program Files\\Smart card bundle\\engine_pkcs11.dll"

MODULE_PATH = C:\\Windows\\System32\\DMPKCS11.dll

init = 0

?

[req]

distinguished_name = req_distinguished_name

?

[req_distinguished_name]

?

可以通過下面命令驗證配置文件并沒有寫錯,openssl識別出了pkcs11 engine,并且生成了證書請求。

C:\Program Files\Smart card bundle>openssl req -config openssl.conf -engine pkcs11 -new -key id_37af001ddbd525e640ca3c3f6d78b009741d1f48 -keyform engine -out req.pem -text -x509 -subj "/CN=Andreas Jellinghaus"

engine "pkcs11" set.

PKCS#11 token PIN:

?

所以最后還是使用動態(tài)調(diào)用的方式導(dǎo)入pkcs11 engine,即ENGINE_load_dynamic。所以兩次調(diào)用ListEngine()后發(fā)現(xiàn), dynamic engine導(dǎo)入pkcs11 engine后就會被其替換。

導(dǎo)入前

id: dynamic, name: Dynamic engine loading support

導(dǎo)入后

id: pkcs11, name: pkcs11 engine

?

2. 導(dǎo)出USBKEY中的CA證書

需要導(dǎo)出CA證書,這是因為CA需要填充X.509格式中的證書的發(fā)行機(jī)構(gòu)名稱。

通過” LOAD_CERT_CTRL”命令來獲取證書,輸入的參數(shù)為證書的表示。

"slot_0-id_37af001ddbd525e640ca3c3f6d78b009741d1f48"

slot_0 ??? PKCS#11 表示的第一個插槽(一個插槽配一個Token)

id_37af001ddbd525e640ca3c3f6d78b009741d1f48 證書的Id號(同一組公私鑰對和證書這個ID是相同的),這個ID可以通過pkcs11-tools獲得。

命令返回的parms.cert就指向一個X.509結(jié)構(gòu)的證書。

但是必須要注意的是導(dǎo)出證書前,必須設(shè)置過正確的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生成的證書請求,直接復(fù)制粘貼到工程目錄下的文本文件certreq.txt即可,并且需要包含BEGIN/END部分。

?

4. 從證書請求中獲取用戶信息

??? //設(shè)置證書的主體名稱,req就是剛剛生成的請求證書

??? X509_set_subject_name(m_pClientCert, X509_REQ_get_subject_name(req));

??? //設(shè)置證書的公鑰信息

??? X509_set_pubkey(m_pClientCert, X509_PUBKEY_get(req->req_info->pubkey));

?

5. 設(shè)置證書的簽發(fā)者信息

??? //設(shè)置證書的簽發(fā)者信息,m_pCACert是CA證書

??? X509_set_issuer_name(m_pClientCert, X509_get_subject_name(m_pCACert));

?

6. 證書簽名

注意這里采用的是sha1的摘要算法,當(dāng)然也可使用MD5

??? //設(shè)置簽名值

??? // EVP_sha1 是否可以設(shè)置成別的,如EVP_md5

??? // 這樣一份X509證書就生成了,下面的任務(wù)就是對它進(jìn)行編碼保存。

??? X509_sign(m_pClientCert, m_pCAKey, EVP_sha1());

?

此處還有些補(bǔ)充的內(nèi)容,為了驗證X509_sign調(diào)用PKCS#11接口的情況,自己實現(xiàn)了一個PKCS#11的包裝殼(68個導(dǎo)出函數(shù)),實現(xiàn)時注意C_GetFunctionList應(yīng)該指向本包裝殼的函數(shù),不然錯誤的使用實際的C_GetFunctionList作返回結(jié)構(gòu)便也就失去意義了。X509_sign的調(diào)用方式還是不同的,java中如果使用SHA1WithRSA傳入到PKCS#11接口的C_Sign或者C_SignUpdate的數(shù)據(jù)是完整的明文,但是X509_sign傳入的是一個ASN.1 Sequence的一個結(jié)構(gòu),結(jié)構(gòu)中包含待簽名數(shù)據(jù)的摘要散列。

舉例來說:

?

待加密的數(shù)據(jù)是Hello World! ,在C_Sign傳入的數(shù)據(jù)中就可以發(fā)現(xiàn)Hello World!的SHA-1的摘要散列。

待加密:Hello World!

SHA-1: 2EF7BDE608CE5404E97D5F042F95F89F1C232871

?

C_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的實現(xiàn)也可以看的這一點,在RSA_Sign之前首先進(jìn)行摘要算法,并且這個摘要并不使用PKCS#11中的接口函數(shù),直接使用Openssl自己的摘要算法,所以傳入到最后的只是明文摘要散列了。

int X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md)
{
//先進(jìn)行ret->cert_info->signature,以及ret->sig_alg的設(shè)置;
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,&(m[0]),&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申請的,所以處于第四級,使用第四級證書來簽發(fā)新證書,”?超人”寶寶就只能到第五級去了(也許是第五項修煉吧)。

其實還是個問題,第四級證書報“此證書似乎對于所選的目的是有效。”,出現(xiàn)此問題的原因嵌入在消息中的簽名證書鏈包含一個無效的交叉引用,估計第四級是一個用戶證書,要消除這個感嘆號,第四級證書的證書用法中應(yīng)該包含Digital Signature, Certificate Signing, Off-line CRL Signing, CRL Signing (86)這幾項

8.完整代碼

// SignWithOpenSSL.cpp :?定義控制臺應(yīng)用程序的入口點。

//

#include "stdafx.h"

#include <windows.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <openssl/engine.h>

#include <openssl/conf.h>

#include <openssl/pem.h>

#include <openssl/x509.h>

#include <openssl/rsa.h>

#include <openssl/x509v3.h>

#define OPENSSL_LOAD_CONF

#define UC_ENGINE_SOPATH "C:\\Program Files\\Smart card bundle\\engine_pkcs11.dll"

#define UC_EXPECTED_ENGINE_ID "pkcs11"

#define UC_ENGINE_MODULEPATH "C:\\Windows\\System32\\DMPKCS11.dll"

//?列出當(dāng)前所有的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: %s\n",

?????????? ENGINE_get_id(current),

?????????? ENGINE_get_name(current));

?????? while( NULL != (current = ENGINE_get_next(current)))

?????? {

?????????? printf("id: %s, name: %s\n",

????????????? 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 borned\n");

??? 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中的私鑰進(jìn)行簽名

??? X509 *m_pClientCert;

??? m_pClientCert = X509_new();

??? //設(shè)置版本號

??? X509_set_version(m_pClientCert, 2);

??? //設(shè)置證書序列號,這個sn就是CA中心頒發(fā)的第N份證書

??? ASN1_INTEGER_set(X509_get_serialNumber(m_pClientCert),100);

??? //設(shè)置證書開始時間

??? X509_gmtime_adj(X509_get_notBefore(m_pClientCert),0);

??? //設(shè)置證書結(jié)束時間

??? X509_gmtime_adj(X509_get_notAfter(m_pClientCert), (long)60*60*24);

??? //設(shè)置證書的主體名稱,req就是剛剛生成的請求證書

??? X509_set_subject_name(m_pClientCert, X509_REQ_get_subject_name(req));

??? //設(shè)置證書的公鑰信息

??? X509_set_pubkey(m_pClientCert, X509_PUBKEY_get(req->req_info->pubkey));

??? //設(shè)置證書的簽發(fā)者信息,m_pCACert是CA證書

????X509_set_issuer_name(m_pClientCert, X509_get_subject_name(m_pCACert));

??? //設(shè)置擴(kuò)展項目

??? 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);

??? //設(shè)置簽名值

??? // EVP_sha1?是否可以設(shè)置成別的,如EVP_md5

??? //?這樣一份X509證書就生成了,下面的任務(wù)就是對它進(jìn)行編碼保存。

????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的配置文件來導(dǎo)入PKCS11

??? ENGINE_load_dynamic();

??? ListEngine();

??? printf("\nLoading Dynamic...\n");

??? /* Register engine */

??? printf("Registering enginen");

??? e = ENGINE_by_id("dynamic");

??? if(!e) {

?????? /* the engine isn't available */

?????? printf("The engine isn't available\n");

?????? 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);

??? //?設(shè)置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_cmd_string(e, "LIST_ADD", "1", 0);

??? ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0);

??? //?設(shè)置USBKEY廠商PKCS#11實現(xiàn)的路徑

??? ENGINE_ctrl_cmd_string(e, "MODULE_PATH", UC_ENGINE_MODULEPATH, 0);

??? //?設(shè)置PIN碼

??? if(!ENGINE_ctrl_cmd_string(e, "PIN", "111111", 0)){

?????? printf("Error sending PIN to = engine");

?????? ENGINE_free(e);

?????? return 0;

??? }??

??? ListEngine();

??? if(!ENGINE_init(e)) {

?????? /* the engine couldn't initialise, release 'e' */

?????? printf("The engine couldn't initialise\n");

?????? ENGINE_free(e);

?????? return 0;

??? }

??? if(!ENGINE_register_RSA(e)){

?????? /* This should only happen when 'e' can't initialise, but the previous

?????? * statement suggests it did. */

?????? printf("This should not happen\n");

?????? abort();

??? }

??? //?直接從usb-key中導(dǎo)入證書,但必須初始化后。

??? 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);

??? // Load private key

??? printf("Loading private key\n");

??? priv_key = ENGINE_load_private_key(e, key_id, ui_method, &cb_data);

??? //?產(chǎn)生證書

??? CreateX509Cert(parms.cert, priv_key);

??? // Release the functional reference from ENGINE_init()

??? ENGINE_finish(e);

??? // Release the structural reference from ENGINE_by_id()

??? ENGINE_free(e);

??? return 0;

}

?

1.4??????????????參考

[1]?DNSSEC Signers and OpenSSL

[END]


總結(jié)

以上是生活随笔為你收集整理的Openssl和PKCS#11的故事的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。