openssl - 數字證書的編程解析


 

原文鏈接: http://www.cangfengzhe.com/wangluoanquan/37.html

這篇文章主要介紹PKI公鑰體系中非常核心元素——數字證書的編程解析。在SSL,SET等安全協議通信時,數字證書用於通信雙方進行身份認證,並且依靠數字證書和非對稱加密算法加密傳輸數據,或者根據數字證書協商通信雙方的共享密鑰。所以,用戶想要開發自己的應用,實現身份認證,必須對數字證書進行解析。根據解析結果,符合一定條件的終端用戶,才可以接入。

1、證書格式介紹

現有的數字證書大都采用了X.509規范,主要由一下信息組成:版本號,證書序列號,有效期(證書生效時間和失效時間),用戶信息(姓名、單位、組織、城市、國家等),頒發者信息,其他擴展信息,擁有者的公鑰,CA對證書整體的簽名,如圖1所示。

X.509 V3數字證書格式

圖1 X.509 V3數字證書格式

OPENSSL開發包中實現了對X.509證書解析的所有操作,如獲得證書的版本、公鑰、擁有者信息、頒發者信息、有效期等,下面就向大家介紹如何通過編程,解析出我們需要的證書信息。

2、證書解析編程實現

2.1 數據結構介紹

X.509證書在OPENSSL中定了專門的數據結構,方便用戶對其操作,其結構如下所示:
struct x509_st
    {
        X509_CINF *cert_info;                    
        X509_ALGOR *sig_alg;                   
        ASN1_BIT_STRING *signature;                  
    int valid;
        int references;
        char *name;
        CRYPTO_EX_DATA ex_data;
        long ex_pathlen;
        long ex_pcpathlen;
        unsigned long ex_flags;
        unsigned long ex_kusage;
        unsigned long ex_xkusage;
        unsigned long ex_nscert;
        ASN1_OCTET_STRING *skid;
        struct AUTHORITY_KEYID_st *akid;
        X509_POLICY_CACHE *policy_cache;
#ifndef OPENSSL_NO_SHA
        unsigned char sha1_hash[SHA_DIGEST_LENGTH];
#endif 
        X509_CERT_AUX *aux;
        };

該結構表示了一個完整的數字證書。各項意義如下:
cert_info:證書主體信息;
sig_alg:簽名算法;
signature:簽名值,存放CA對該證書簽名的結果;
valid:是否是合法證書,1為合法,0為未知;
references:引用次數,被引用一次則加一;
name:證書持有者信息;
ex_data:擴展數據結構,用於存放用戶自定義的信息;
ex_pathlen:證書路徑長度;
ex_kusage:密鑰用法;
ex_xkusage:擴展密鑰用法;
ex_nscert:Netscape證書類型;
skid:主體密鑰標識;
akid:頒發者密鑰標識;
policy_cache:各種策略緩存;
sha1_hash:存放證書的sha1摘要值;
aux:輔助信息;

其中,證書主體信息—X509_CINF結構體定義如下:
typedef struct x509_cinf_st
    {
        ASN1_INTEGER *version;                                       //證書版本
        ASN1_INTEGER *serialNumber;                              //序列號
        X509_ALGOR *signature;                                        //簽名算法  
        X509_NAME *issuer;                                               //頒發者     
        X509_VAL *validity;                                                // 有效時間  
        X509_NAME *subject;                                            // 持有者     
        X509_PUBKEY *key;                                              // 公鑰  
        ASN1_BIT_STRING *issuerUID;                          // 頒發者唯一標識    
        ASN1_BIT_STRING *subjectUID;                        // 持有者唯一標識     
        STACK_OF(X509_EXTENSION) *extensions;         // 擴展項     
    } X509_CINF;

2.2 函數介紹

根據上述結構體可知,我們可以通過編程,讀取結構體中的證書信息,下面介紹一下幾個常用的函數。

(1)編碼轉換函數

數字證書分為DER編碼和PEM編碼,所以對應的操作是不一樣的。對於DER編碼的證書,我們可以通過函數:X509 * d2i_X509(x509 **cert , unsigned char **d , int len),返回一個X.509的結構體指針。而對於PEM編碼的證書,我沒找到一個函數來實現編碼轉換,但可以通過OPENSSL提供的BIO函數,實現這一功能:先調用BIO_new_file() 返回一個BIO結構體,然后通過 PEM_read_bio_X509() 返回一個X.509結構體。

(2)獲得證書信息

其實獲得證書信息的操作,僅僅是解析X509和X509_CINF結構體的操作,可以得到如:證書版本,頒發者信息,證書擁有者信息,有效期,證書公鑰等信息,主要函數如下:
X509_get_version();              //獲得證書版本;
X509_get_issuer_name();          //獲得證書頒發者信息
X509_get_subjiect_name();        //獲得證書擁有者信息
X509_get_notBefore();           //獲得證書起始日期
X509_get_notAfter();            //獲得證書終止日期
X509_get_pubkey();             //獲得證書公鑰

其中,函數具體的參數和使用,結合下面編程代碼向大家介紹。

2.3編程實現

通過上述的函數和結構體的介紹,下面編程實現解析一個數字證書就非常簡單了。在此,我編寫了一個解析證書的軟件,實現關鍵代碼如下:
fp=fopen(filename.GetBuffer(0),"rb");
if(fp==NULL)
{
MessageBox("讀取證書錯誤");
return ;
}
Certlen=fread(Cert,1,4096,fp);
fclose(fp);
//判斷是否為DER編碼的用戶證書,並轉化為X509結構體
pTmp=Cert;
usrCert = d2i_X509(NULL,(const unsigned char ** )&pTmp,Certlen);
if(usrCert==NULL)
{
BIO *b;
/* 判斷是否為PEM格式的數字證書 */
b=BIO_new_file(filename.GetBuffer(0),"r");
usrCert=PEM_read_bio_X509(b,NULL,NULL,NULL);
BIO_free(b);
if(usrCert==NULL)
{
MessageBox("轉化格式錯誤!");
    return;
}
}
//解析證書
X509_NAME *issuer = NULL;//X509_NAME結構體,保存證書頒發者信息
X509_NAME *subject = NULL;//X509_NAME結構體,保存證書擁有者信息
//獲取證書版本
Version = X509_get_version(usrCert);
//獲取證書頒發者信息,X509_NAME結構體保存了多項信息,包括國家、組織、部門、通用名、mail等。
issuer = X509_get_issuer_name(usrCert);
//獲取X509_NAME條目個數
entriesNum = sk_X509_NAME_ENTRY_num(issuer->entries);
//循環讀取各條目信息
for(i=0;i<entriesNum;i++)
{
//獲取第I個條目值
name_entry = sk_X509_NAME_ENTRY_value(issuer->entries,i);
//獲取對象ID
Nid = OBJ_obj2nid(name_entry->object);
//判斷條目編碼的類型
if(name_entry->value->type==V_ASN1_UTF8STRING)
//把UTF8編碼數據轉化成可見字符
{
nUtf8 = 2*name_entry->value->length;
pUtf8 = (unsigned short *)malloc(nUtf8);
memset(pUtf8,0,nUtf8);
rv = MultiByteToWideChar(
CP_UTF8,
0,
(char*)name_entry->value->data,
name_entry->value->length,
pUtf8,
nUtf8);
rv = WideCharToMultiByte(
CP_ACP,
0,
pUtf8,
rv,
(char*)msginfo,
nUtf8,
NULL,
NULL);
free(pUtf8);
pUtf8 = NULL;
msginfoLen = rv;
msginfo[msginfoLen]='\0';
}
else
{
msginfoLen=name_entry->value->length;
memcpy(msginfo,name_entry->value->data,msginfoLen);
msginfo[msginfoLen]='\0';
}
//根據NID打印出信息
switch(Nid)
{
case NID_countryName://國家
tmp.Format("issuer 's countryName:%s\n",msginfo);
        m_list.InsertString(-1,tmp);
        tmp.Empty();
break;
case NID_stateOrProvinceName://省
tmp.Format("issuer 's ProvinceName:%s\n",msginfo);
        m_list.InsertString(-1,tmp);
        tmp.Empty();
break;
case NID_localityName://地區
tmp.Format("issuer 's localityName:%s\n",msginfo);
        m_list.InsertString(-1,tmp);
        tmp.Empty();
break;
case NID_organizationName://組織
tmp.Format("issuer 's organizationName:%s\n",msginfo);
        m_list.InsertString(-1,tmp);
        tmp.Empty();
break;
case NID_organizationalUnitName://單位
tmp.Format("issuer 's organizationalUnitName:%s\n",msginfo);
        m_list.InsertString(-1,tmp);
        tmp.Empty();
break;
case NID_commonName://通用名
tmp.Format("issuer 's commonName:%s\n",msginfo);
        m_list.InsertString(-1,tmp);
        tmp.Empty();
break;
case NID_pkcs9_emailAddress://Mail
tmp.Format("issuer 's emailAddress:%s\n",msginfo);
        m_list.InsertString(-1,tmp);
        tmp.Empty();
break;
}//end switch
}
//獲取證書主題信息,與前面類似,在此省略
subject = X509_get_subject_name(usrCert);
………

//獲取證書生效日期
time = X509_get_notBefore(usrCert);
tmp.Format("Cert notBefore:%s\n",time->data);
m_list.InsertString(-1,tmp);
tmp.Empty();
//獲取證書過期日期
time = X509_get_notAfter(usrCert);
tmp.Format("Cert notAfter:%s\n",time->data);
m_list.InsertString(-1,tmp);
tmp.Empty();
//獲取證書公鑰
pubKey = X509_get_pubkey(usrCert);
pTmp=derpubkey;
//把證書公鑰轉為DER編碼的數據
derpubkeyLen=i2d_PublicKey(pubKey,&pTmp);
printf("PublicKey is: \n");
for(i = 0; i < derpubkeyLen; i++)
{
CString tmpp;
tmpp.Format("%02x", derpubkey[i]);
tmp=tmp+tmpp;
}
m_list.InsertString(-1,tmp);
   //釋放結構體內存
X509_free(usrCert);

使用軟件解析證書效果,如下圖所示:

證書解析效果

圖2 解析效果

3、結束語

通過上述介紹,相信大家對數字證書又有了更進一步的了解。在開發網絡通信軟件,需要加入身份認證功能時,大家可以根據解析出來的證書信息,與自己的訪問策略相對比,實現訪問控制。

數字證書在信息安全中處於重要地位,隨着密碼學的發展,數字證書的應用也會越來越廣。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM