Openssl之PEM系列

Openssl之PEM系列

創(chuàng)新互聯(lián)專注于如皋網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗。 熱誠為您提供如皋營銷型網(wǎng)站建設(shè),如皋網(wǎng)站制作、如皋網(wǎng)頁設(shè)計、如皋網(wǎng)站官網(wǎng)定制、小程序定制開發(fā)服務(wù),打造如皋網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供如皋網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。

1.PEM編碼文件結(jié)構(gòu)介紹

PEM全稱是Privacy Enhanced Mail,該標(biāo)準(zhǔn)定義了加密一個準(zhǔn)備要發(fā)送郵件的標(biāo)準(zhǔn),主要用來將各種對象保存成PEM格式,并將PEM格式的各種對象讀取到相應(yīng)的結(jié)構(gòu)中。它的基本流程是這樣的:

  1. 信息轉(zhuǎn)換為ASCII碼或其它編碼方式;
  2. 使用對稱算法加密轉(zhuǎn)換了的郵件信息;
  3. 使用BASE64對加密后的郵件信息進行編碼;
  4. 使用一些頭定義對信息進行封裝,這些頭信息格式如下(不一定都需要,可選的):

Proc-Type,4:ENCRYPTED

DEK-Info: cipher-name, ivec其中,第一個頭信息標(biāo)注了該文件是否進行了加密,該頭信息可能的值包括ENCRYPTED(信息已經(jīng)加密和簽名)、MIC-ONLY(信息經(jīng)過數(shù)字簽名但沒有加密)、MIC-CLEAR(信息經(jīng)過數(shù)字簽名但是沒有加密、也沒有進行編碼,可使用非PEM格式閱讀)以及CLEAR(信息沒有簽名和加密并且沒有進行編碼,該項好象是openssl自身的擴展,但是并沒有真正實現(xiàn));;第二個頭信息標(biāo)注了加密的算法以及使用的ivec參量,ivec其實在這兒提供的應(yīng)該是一個隨機產(chǎn)生的數(shù)據(jù)序列,與塊加密算法中要使用到的初始化變量(IV)不一樣。

  1. 在這些信息的前面加上如下形式頭標(biāo)注信息:

—–BEGIN PRIVACY-ENHANCED MESSAGE—–

在這些信息的后面加上如下形式尾標(biāo)注信息:

—–END PRIVACY-ENHANCED MESSAGE—–

上面是openssl的PEM文件的基本結(jié)構(gòu),需要注意的是,Openssl并沒有實現(xiàn)PEM的全部標(biāo)準(zhǔn),它只是對openssl中需要使用的一些選項做了實現(xiàn),詳細的PEM格式,請參考RFC1421-1424。

下面是一個PEM編碼的經(jīng)過加密的DSA私鑰的例子:

—–BEGIN DSA PRIVATE KEY—–

Proc-Type: 4,ENCRYPTED

DEK-Info: DES-EDE3-CBC,F80EEEBEEA7386C4

GZ9zgFcHOlnhPoiSbVi/yXc9mGoj44A6IveD4UlpSEUt6Xbse3Fr0KHIUyQ3oGnSmClKoAp/eOTb5Frhto85SzdsxYtac+X1v5XwdzAMy2KowHVk1N8A5jmE2OlkNPNtof132MNlo2cyIRYaa35PPYBGNCmUm7YcYS8O90YtkrQZZTf4+2C4kllhMcdkQwkrFWSWC8YOQ7w0LHb4cX1FejHHom9Nd/0PN3vn3UyySvfOqoR7nbXkrpHXmPIr0hxXRcF0aXcV/CzZ1/nfXWQf4o3+oD0T22SDoVcZY60IzI0oIc3pNCbDV3uKNmgekrFdqOUJ+QW8oWp7oefRx62iBfIeC8DZunohMXaWAQCU0sLQOR4yEdeUCnzCSywe0bG1diD0KYaEe+Yub1BQH4aLsBgDjardgpJRTQLq0DUvw0/QGO1irKTJzegEDNVBKrVnV4AHOKT1CUKqvGNRP1UnccUDTF6miOAtaj/qpzra7sSk7dkGBvIEeFoAg84kfh9hhVvF1YyzC9bwZepruoqoUwke/WdNIR5ymOVZ/4Liw0JdIOcq+atbdRX08niqIRkfdsZrUj4leo3zdefYUQ7w4N2Ns37yDFq7

—–END DSA PRIVATE KEY—–

有時候PEM編碼的東西并沒有經(jīng)過加密,只是簡單進行了BASE64編碼,下面是一個沒有加密的證書請求的例子:

—–BEGIN CERTIFICATE REQUEST—–

MIICVTCCAhMCAQAwUzELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEMMAoGA1UEAxMDUENBMIIBtTCCASkGBSsOAwIMMIIBHgKBgQCnP26Fv0FqKX3wn0cZMJCaCR3aajMexT2GlrMV4FMuj+BZgnOQPnUxmUd6UvuF5NmmezibaIqEm4fGHrV+hktTW1nPcWUZiG7OZq5riDb77Cjcwtelu+UsOSZL2ppwGJU3lRBWI/YV7boEXt45T/23Qx+1pGVvzYAR5HCVW1DNSQIVAPcHMe36bAYD1YWKHKycZedQZmVvAoGATd9MA6aRivUZb1BGJZnlaG8w42nh6bNdmLsohkj83pkEP1+IDJxzJA0gXbkqmj8YlifkYofBe3RiU/xhJ6h7kQmdtvFNnFQPWAbuSXQHzlV+I84W9srcWmEBfslxtU323DQph3j2XiCTs9v15AlsQReVkusBtXOlan7YMu0OArgDgYUAAoGBAKbtuR5AdW+ICjCFe2ixjUiJJzM2IKwe6NZEMXg39+HQ1UTPTmfLZLps+rZfolHDXuRKMXbGFdSF0nXYzotPCzi7GauwEJTZyr27ZZjA1C6apGSQ9GzuwNvZ4rCXystVEagAS8OQ4H3D4dWS17Zg31ICb5o4E5r0z09o/Uz46u0VoAAwCQYFKw4DAhsFAAMxADAuAhUArRubTxsbIXy3AhtjQ943AbNBnSICFQCu+g1iW3jwF+gOcbroD4S/ZcvB3w==

—–END CERTIFICATE REQUEST—–

可以看到,該文件沒有了前面兩個頭信息。大家如果經(jīng)常使用openssl的應(yīng)用程序,就對這些文件格式很熟悉了。

2.PEM類型和實現(xiàn)結(jié)構(gòu)介紹

openssl中定義的PEM相關(guān)結(jié)構(gòu)體如下(openssl\pem.h),這些結(jié)構(gòu)體是所有PEM系列函數(shù)的基礎(chǔ)。

下面定義的是PEM一個高層應(yīng)用結(jié)構(gòu),該結(jié)構(gòu)通過PEM_SealInit進行初始化,最后使用PEM_SealFinal進行釋放,該結(jié)構(gòu)定義了PEM中要使用的編碼算法、信息摘要算法以及加密算法。

typedef struct PEM_Encode_Seal_st

{

EVP_ENCODE_CTX encode;

EVP_MD_CTX md;

EVP_CIPHER_CTX cipher;

} PEM_ENCODE_SEAL_CTX;

下面定義了PEM_CTX中的一個子結(jié)構(gòu),用來保存用戶的信息

typedef struct pem_recip_st

{

char *name;

X509_NAME *dn;

int cipher;

int key_enc;

} PEM_USER;

下面是PEM主結(jié)構(gòu)體PEM_CTX結(jié)構(gòu)的定義,我們將在注釋里面對必要的參數(shù)進行說明。

typedef struct pem_ctx_st

{

int type;//結(jié)構(gòu)類型

struct

{

int version;//版本號

int mode;//編碼方式

} proc_type;//Proc_Type字段信息,包括版本號和編碼方式

char *domain;

struct

{

int cipher;

} DEK_info;//定義了PEM中DEK_info字段的信息

PEM_USER *originator;

int num_recipient;

PEM_USER **recipient;

#ifndef OPENSSL_NO_STACK

STACK *x509_chain;//保存證書鏈

#else

char *x509_chain; //保存證書鏈

#endif

EVP_MD *md; //簽名算法類型,指定了信息摘要算法和簽名算法

int md_enc; //信息摘要算法是否進行了加密(簽名)

int md_len; //摘要信息的長度

char *md_data; //摘要信息,可以是經(jīng)過了加密(簽名)的信息

EVP_CIPHER *dec;//數(shù)據(jù)加密算法

int key_len; //密鑰長度

unsigned char *key; //加密密鑰

int data_enc; //數(shù)據(jù)是否加密標(biāo)志

int data_len; //數(shù)據(jù)長度

unsigned char *data; //數(shù)據(jù)

} PEM_CTX;

下面我們對PEM_CTX結(jié)構(gòu)體中一些重要的參數(shù)做詳細的說明

2.1 int type參數(shù)

該參數(shù)指明了PEM_CTX結(jié)構(gòu)的類型,目前包括了以下定義的類型:

#define PEM_OBJ_UNDEF 0

#define PEM_OBJ_X509 1

#define PEM_OBJ_X509_REQ 2

#define PEM_OBJ_CRL 3

#define PEM_OBJ_SSL_SESSION 4

#define PEM_OBJ_PRIV_KEY 10

#define PEM_OBJ_PRIV_RSA 11

#define PEM_OBJ_PRIV_DSA 12

#define PEM_OBJ_PRIV_DH 13

#define PEM_OBJ_PUB_RSA 14

#define PEM_OBJ_PUB_DSA 15

#define PEM_OBJ_PUB_DH 16

#define PEM_OBJ_DHPARAMS 17

#define PEM_OBJ_DSAPARAMS 18

#define PEM_OBJ_PRIV_RSA_PUBLIC 19

可以看到,這些類型基本上包括了所有openssl中要使用的基本結(jié)構(gòu)

2.2 struct proc_type參數(shù)

該參數(shù)是保存了PEM標(biāo)準(zhǔn)中Proc_Type字段的信息(參考《openssl之PEM系列之1》),可以看到,該結(jié)構(gòu)包括兩個字段,第一個字段version是版本號,第二個字段mode是信息的編碼方式,目前定義了四種,如下:

#define PEM_TYPE_ENCRYPTED 10

#define PEM_TYPE_MIC_ONLY 20

#define PEM_TYPE_MIC_CLEAR 30

#define PEM_TYPE_CLEAR 40

這四個值的意義可以參考《openssl之PEM系列之1》。值得注意是,在openssl實現(xiàn)的PEM文件中,最后一個PEM_TYPE_CLEAR其實并沒有用到。

2.3 struct DEK_info參數(shù)

該參數(shù)定義了PEM中DEK_info字段的信息,本來該參數(shù)應(yīng)該含有兩個字段,包括加密算法和IV。但是由于歷史原因,openssl中原有的非標(biāo)準(zhǔn)的IV字段在新版的openssl中取消了,所以就剩下一個算法定義了,目前支持的算法如下述的定義:

#define PEM_DEK_DES_CBC 40

#define PEM_DEK_IDEA_CBC 45

#define PEM_DEK_DES_EDE 50

#define PEM_DEK_DES_ECB 60

#define PEM_DEK_RSA 70

#define PEM_DEK_RSA_MD2 80

#define PEM_DEK_RSA_MD5 90

3.PEM系列函數(shù)通用參數(shù)介紹

PEM系列函數(shù)中很多參數(shù)是相同意義的,也就是說通用的。本節(jié)將對這些通用參數(shù)的意義進行介紹,以便于后述章節(jié)能夠更方便流暢地進行PEM系列函數(shù)的介紹。

3.1 bp參數(shù)

如果函數(shù)有該參數(shù),則定義了進行數(shù)據(jù)讀寫B(tài)IO接口。

3.2 fp參數(shù)

如果函數(shù)包含了該參數(shù),則定義了進行數(shù)據(jù)讀寫的FILE指針。

3.3 TYPE類型參數(shù)

PEM讀操作的系列函數(shù)都有TYPE **x 和返回TYEP *指針的參數(shù)。這里的TYPE可以為任何函數(shù)要使用的結(jié)構(gòu)體,如DSA或X509之類的。如果參數(shù)x是NULL,那么該參數(shù)將被忽略。如果x不是NULL,但是*x是NULL,那么返回的結(jié)構(gòu)體就會寫入到*x里面。如果x和*x都不是NULL,那么函數(shù)就試圖重用*x中的結(jié)構(gòu)體。這中函數(shù)總是返回一個執(zhí)行結(jié)構(gòu)體的指針(x的值),如果出錯,就返回NULL。

3.4 enc參數(shù)

enc參數(shù)定義了PEM函數(shù)寫私鑰的時候采用的加密算法。加密是在PEM層進行的。如果該參數(shù)為NULL,那么私鑰就會以不加密的形式寫入相應(yīng)的接口。

3.5 cb參數(shù)

cb參數(shù)定義了回調(diào)函數(shù),該回調(diào)函數(shù)在加密PEM結(jié)構(gòu)體(一般來說是私鑰)需要口令的時候使用。

3.6 kstr參數(shù)

主要在PEM寫系列函數(shù)里面使用,如果該參數(shù)不為NULL,那么kstr中klen字節(jié)數(shù)據(jù)就用來作為口令,此時,cb參數(shù)就被忽略了。

3.7 u參數(shù)

如果cb參數(shù)為NULL,而u參數(shù)不為NULL,那么u參數(shù)就是一個以NULL結(jié)束的字符串用作口令。如果cb和u參數(shù)都是NULL,那么缺省的回調(diào)函數(shù)就會并使用,該函數(shù)一般在當(dāng)前的終端提示輸入口令,并且關(guān)掉了回顯功能。

3.8回調(diào)函數(shù)callback函數(shù)介紹

因為缺省的回調(diào)函數(shù)基于終端的,有時候不適合使用(如GUI程序),所以可以使用替換的回調(diào)函數(shù)?;氐胶瘮?shù)的形式如下:

int cb(char *buf, int size, int rwflag, void *u);

在該函數(shù)中,buf是保存口令的參數(shù)。size是考慮最大的長度(如buf的長度)。rwflag是一個讀寫標(biāo)志,0的時候為讀操作,1的時候為寫操作。當(dāng)rwflag為1的時候,典型的函數(shù)一般會要求用戶驗證口令(如輸入兩次)。u參數(shù)跟上述PEM函數(shù)的u參數(shù)意義是一樣的,它允許應(yīng)用程序使用固定的數(shù)據(jù)作為參數(shù)傳給回調(diào)函數(shù)?;卣{(diào)函數(shù)必須返回口令字符的數(shù)目,如果出錯返回0。

4.PEM結(jié)構(gòu)信息處理函數(shù)

本次介紹的函數(shù)是處理PEM結(jié)構(gòu)里面一些字段信息的函數(shù),這些函數(shù)在一般應(yīng)用中可能不會用到,但是深入一點的應(yīng)用,恐怕就避免不了。此外,了解這些應(yīng)用,對于加深對PEM結(jié)構(gòu)的理解也是很有好處的。下面是其中相關(guān)一些函數(shù)的定義(openssl\pem.h):

int PEM_get_EVP_CIPHER_INFO(char *header, EVP_CIPHER_INFO *cipher);

int PEM_do_header (EVP_CIPHER_INFO *cipher, unsigned char *data,long *len,pem_password_cb *callback,void *u);

void PEM_proc_type(char *buf, int type);

void PEM_dek_info(char *buf, const char *type, int len, char *str);

4.1 PEM_proc_type

該函數(shù)是通過給定參數(shù)type返回一個標(biāo)準(zhǔn)的PEM文件的Proc-Type字段信息。返回的信息寫入到buf參數(shù)里面去,所以要求buf分配的內(nèi)存空間必須足夠大。事實上,該函數(shù)返回的字符串不外乎下面四種結(jié)果:

當(dāng)type為PEM_TYPE_ENCRYPTED,返回字符串為”Proc-Type: 4,ENCRYPTED\n”

當(dāng)type為PEM_TYPE_MIC_CLEAR,返回字符串為”Proc-Type: 4,MIC-CLEAR\n”

當(dāng)type為PEM_TYPE_MIC_ONLY,返回字符串為”Proc-Type: 4,MIC-ONLY\n”

當(dāng)type為其它值時,返回字符串為 “Proc-Type: 4,BAD-TYPE\n”

事實上,雖然上字段信息中有MIC(信息摘要)選項,但openssl的PEM庫并沒有實現(xiàn)MIC計算的功能。當(dāng)然,可以通過使用RSA-MD系列函數(shù)將PEM的數(shù)據(jù)信息進行摘要并將該結(jié)果作為PEM的MIC。你可以通過PEM_dek_info函數(shù)產(chǎn)生MIC-info頭信息,然后寫入到PEM結(jié)構(gòu)中,不過據(jù)openssl的說明,這需要的時間可能會比較長,大概5分鐘左右。

4.2 PEM_dek_info

該函數(shù)跟上述函數(shù)相似,是根據(jù)type參數(shù)生成DEK-info字段的信息,返回并寫入到buf里面。參數(shù)str里應(yīng)該是提供了ivec變量的值,參數(shù)len是str的長度(單位是字節(jié))。在這里,參數(shù)type應(yīng)該為加密算法的名字,原則上這個字符串可以是任意的,但是為了其它程序能夠正確解釋該字段,你可以先得到算法相應(yīng)的NID,然后通過調(diào)用nid2sn得到該算法的簡稱作為type參數(shù)。例如我們需要在PEM_ASN1_write_bio中使用算法結(jié)構(gòu)enc,那么可以調(diào)用下面函數(shù):

objstr=OBJ_nid2sn(EVP_CIPHER_nid(enc));

此時objstr就是一個包含了算法enc的簡稱的字符串。然后我們就可以通過下面的語句在PEM_dek_info函數(shù)中使用這個字符串了:

PEM_dek_info(buf,objstr,8,(char *)iv);

4.3 PEM_do_header

該函數(shù)并非顧名思義,事實上它完成了對一個PEM編碼對象的的解密工作(如果該PEM對象需要進行解密),該函數(shù)通常是被PEM_read_bio所調(diào)用的。在調(diào)用該函數(shù)之前,應(yīng)該已經(jīng)將PEM文件的一些頭信息得到,以便于正確進行解密操作。其中,DEK-info字段的信息應(yīng)該在調(diào)用本函數(shù)之前進行正確的處理,從而通過該字段的名字和ivec得到相應(yīng)的EVP_CIPHER結(jié)構(gòu)信息和IV變量,作為本函數(shù)的cipher參數(shù)。

如果PEM文件沒有DEK-info字段,那么該函數(shù)簡單返回1,操作成功,因為不需要進行解密操作。如果不是的話,那么該函數(shù)就需要一個口令來進行解密。首先,它會試圖從callback參數(shù)(一個回調(diào)函數(shù))中得到該口令?;卣{(diào)函數(shù)的格式如下:

callback(buffer, blen, verify)

其中,參數(shù)buffer是保存返回口令的地方,blen是buffer的最大長度,verify參數(shù)是指明是否需要口令驗證(就是要求用戶輸入兩次相同的口令),默認(rèn)的是0。

如果callback參數(shù)為NULL,而u參數(shù)不為NULL,那么u參數(shù)就會以NULL為結(jié)束符的字符串作為口令寫入到buffer中;如果callback和u參數(shù)都為NULL,那么就會調(diào)用缺省的callback函數(shù)(關(guān)于u的具體意義,請參考《openssl之PEM系列之3》)。PEM_do_header函數(shù)得到口令后,就使用該口令(包括長度信息)跟cipher參數(shù)種的ivec變量一起對數(shù)據(jù)進行解密。解密后的數(shù)據(jù)保存在data中,長度信息保存在plen中。該函數(shù)操作成功返回1,否則返回0。

4.4 PEM_get_EVP_CIPHER_INFO

該函數(shù)一般也被PEM_read_bio函數(shù)調(diào)用。在調(diào)用該函數(shù)之前,PEM的Proc-Type頭信息應(yīng)該已經(jīng)作為明文被讀入到header參數(shù)中。如果header為NULL,那么函數(shù)成功返回1,因為沒有什么頭信息要處理。如果不為NULL,那么該函數(shù)首先確定header信息是否以“Proc-Type:4,ENCRYPTED”開頭,如果是其它形式的,該函數(shù)將返回0,不進行處理。之后,函數(shù)開始讀取DEK-info字段的信息,然后函數(shù)通過該字段的加密算法名字使用EVP_get_cihperbyname得到一個EVP_CIPHER結(jié)構(gòu),并保存在參數(shù)cipher->cipher中;然后函數(shù)再通過調(diào)用內(nèi)部的函數(shù)得到ivec的值,并保存在cipher->iv中。成功操作返回1,否則返回0。

需要注意的是,因為該函數(shù)調(diào)用了EVP_get_cipherbyname,所以在調(diào)用本函數(shù)前,應(yīng)該先調(diào)用EVP_add_cipher和EVP_add_alias,或者調(diào)用SSLeay_add_all_algorithms,從而將所有加密算法的信息載入到程序中。具體的情況請參考《openssl之EVP系列》相關(guān)章節(jié)。

5.PEM信息封裝加密系列函數(shù)

該系列函數(shù)完成了對PEM對象以及相關(guān)密鑰和IV向量的加密編碼工作,以便于數(shù)據(jù)的保存和傳送,主要包括以下函數(shù)(openssl\pem.h):

int PEM_SealInit(PEM_ENCODE_SEAL_CTX *ctx, EVP_CIPHER *type,EVP_MD *md_type, unsigned char **ek, int *ekl,unsigned char *iv, EVP_PKEY **pubk, int npubk);

void PEM_SealUpdate(PEM_ENCODE_SEAL_CTX *ctx, unsigned char *out, int *outl,unsigned char *in, int inl);

int PEM_SealFinal(PEM_ENCODE_SEAL_CTX *ctx, unsigned char *sig,int *sigl,unsigned char *out, int *outl, EVP_PKEY *priv);

void PEM_SignInit(EVP_MD_CTX *ctx, EVP_MD *type);

void PEM_SignUpdate(EVP_MD_CTX *ctx,unsigned char *d,unsigned int cnt);

int PEM_SignFinal(EVP_MD_CTX *ctx, unsigned char *sigret,unsigned int *siglen, EVP_PKEY *pkey);

void ERR_load_PEM_strings();

其中,PEM_Seal*系列函數(shù)完成了對PEM對象、密鑰和IV變量的加密編碼工作,PEM_Sign系列函數(shù)完成了對PEM進行數(shù)字簽名的工作。

5.1 PEM_SealInit函數(shù)

該函數(shù)為后續(xù)的PEM_SealUpdate和PEM_SealFinal函數(shù)做初始化工作。首先,該函數(shù)使用參數(shù)md_type調(diào)用函數(shù)EVP_SignInit對信息摘要結(jié)構(gòu)ctx->md進行初始化。然后,該函數(shù)通過參數(shù)type找到相應(yīng)的EVP_CIPHER結(jié)構(gòu),產(chǎn)生適用于該算法的密鑰和ivec變量并保存在該算法結(jié)構(gòu)中,然后使用參數(shù)pubk的公鑰調(diào)用函數(shù)EVP_SealInit對該密鑰進行加密。加密后的秘鑰保存在參數(shù)ek里面,其長度保存在ekl里面,這些數(shù)據(jù)都是調(diào)用了EVP_EncodeUpdate函數(shù)經(jīng)過了BASE64編碼的。因為密鑰和IV已經(jīng)保存在ctx->cipher中,所以,可以被后續(xù)的函數(shù)用來對PEM對象進行加密處理。該函數(shù)成功操作返回正值,否則返回0或-1。

需要注意的是,因為本函數(shù)也使用了加密算法名字查找算法結(jié)構(gòu),所以在調(diào)用本函數(shù)之前必須加載該靜態(tài)算法結(jié)構(gòu)棧。

5.2 PEM_SealUpdate函數(shù)

該函數(shù)用來完成對PEM對象信息體的加密和編碼,使用的加密密鑰是PEM_SealInit函數(shù)產(chǎn)生的。該函數(shù)對參數(shù)in中的inl個字節(jié)的數(shù)據(jù)采用ctx->cipher提供的對稱加密算法結(jié)構(gòu)(已經(jīng)包含了密鑰和IV)進行加密操作,然后調(diào)用EVP_EncodeUpdate進行BASE64編碼后保存在參數(shù)out里面,outl是out里有效數(shù)據(jù)的長度信息。在此同時,該函數(shù)也調(diào)用函數(shù)EVP_SignUpdate函數(shù)使用ctx->md的摘要算法結(jié)構(gòu)對參數(shù)in里的數(shù)據(jù)進行了信息摘要操作,不過暫時沒有輸出,等調(diào)用了PEM_SealFinal函數(shù)的時候進行輸出。

需要注意的是,該函對輸入的信息in的長度做了限制,不能大于1200字節(jié),否則將超過1200字節(jié)的信息簡單丟棄。

5.3 PEM_SealFinal函數(shù)

該函數(shù)完成整個PEM_Seal系列的操作。首先,它完成了之前使用PEM_SealUpdate函數(shù)進行處理的數(shù)據(jù)的對稱加密工作,將數(shù)據(jù)進行BASE64編碼并輸出到參數(shù)out,outl保存了out數(shù)據(jù)的有效長度。同時,該函數(shù)還完成了信息摘要工作,并使用參數(shù)priv的私鑰對該信息進行簽名(加密),將結(jié)果經(jīng)過BASE64編碼后輸出到參數(shù)sig,sigl是sig有效數(shù)據(jù)的長度信息。該函數(shù)成功操作返回1,否則返回0。

需要注意的是,該函數(shù)運行完后,就將ctx->md和ctx->cipher結(jié)構(gòu)釋放清除掉了,所以如果你想保存對稱加密算法使用的密鑰和IV的話,你需要在調(diào)用本函數(shù)之前就保存一個備份。當(dāng)然,一般情況下是不會這么做的,因為這些密鑰應(yīng)該是臨時密鑰,只用來加密一個信息。

5.4 PEM_Seal操作總結(jié)

完成上述三個函數(shù)的操作之后,你就得到了加密后的密鑰、IV(從PEM_SealInit函數(shù))以及PEM對象信息體,并且這些都是經(jīng)過BASE64編碼的。然后,你就可以將這些信息發(fā)送給接受方了。對方接受到這些信息后,使用他自己的私鑰以及你的公鑰,就能進行正確的數(shù)據(jù)解密和驗證。

5.5 PEM_SignInit,PEM_SignUpdate和PEM_SignFinal函數(shù)

這三個函數(shù)完成的功能跟EVP_Sign系列函數(shù)是一樣的,其實,前面兩個函數(shù)就簡單調(diào)用了EVP_SignInit和EVP_SignUpdate函數(shù)。PEM_SignFinal則調(diào)用EVP_SignFinal函數(shù)完成信息摘要和簽名(使用參數(shù)pkey的私鑰)之后,調(diào)用了EVP_EncodeBlock對簽名信息進行了BASE64編碼,然后將編碼后的簽名信息保存在參數(shù)sigret,siglen保存了sigret有效數(shù)據(jù)的長度。PEM_SignFinal函數(shù)成功返回1,否則返回0。

5.6 ERR_load_PEM_strings函數(shù)

該函數(shù)使用了PEM庫的錯誤代碼信息對錯誤處理庫進行初始化,必須在使用任何PEM系列函數(shù)之前調(diào)用該函數(shù)。

6.PEM底層IO函數(shù)

PEM提供了一系列底層的進行數(shù)據(jù)讀寫操作的IO函數(shù),在后面章節(jié)敘述到的PEM對象的IO函數(shù)都是這些函數(shù)的宏定義,所以雖然一般不要直接調(diào)用這些函數(shù),做一個清楚的了解還是必要的。這些函數(shù)定義如下(openssl\pem.h):

int PEM_read_bio(BIO *bp, char **name, char **header,unsigned char **data,long *len);

int PEM_write_bio(BIO *bp,const char *name,char *hdr,unsigned char *data,long len);

int PEM_bytes_read_bio(unsigned char **pdata, long *plen, char **pnm, const char *name, BIO *bp,pem_password_cb *cb, void *u);

char *PEM_ASN1_read_bio(char *(*d2i)(),const char *name,BIO *bp,char **x,pem_password_cb *cb, void *u);

int PEM_ASN1_write_bio(int (*i2d)(),const char *name,BIO *bp,char *x,const EVP_CIPHER *enc,unsigned char *kstr,int klen,pem_password_cb *cb, void *u);

STACK_OF(X509_INFO) *PEM_X509_INFO_read_bio(BIO *bp, STACK_OF(X509_INFO) *sk, pem_password_cb *cb, void *u);

int PEM_X509_INFO_write_bio(BIO *bp,X509_INFO *xi, EVP_CIPHER *enc,unsigned char *kstr, int klen, pem_password_cb *cd, void *u);

int PEM_read(FILE *fp, char **name, char **header,unsigned char **data,long *len);

int PEM_write(FILE *fp,char *name,char *hdr,unsigned char *data,long len);

char *PEM_ASN1_read(char *(*d2i)(),const char *name,FILE *fp,char **x,pem_password_cb *cb, void *u);

int PEM_ASN1_write(int (*i2d)(),const char *name,FILE *fp,char *x,const EVP_CIPHER *enc,unsigned char *kstr,int klen,pem_password_cb *callback, void *u);

STACK_OF(X509_INFO) *PEM_X509_INFO_read(FILE *fp, STACK_OF(X509_INFO) *sk,pem_password_cb *cb, void *u);

可以看到,這些函數(shù)中有很多參數(shù)在第3部分介紹過,在此將不再詳細介紹。

6.1 PEM_read函數(shù)

該函數(shù)從文件fp里面讀取一個PEM編碼的信息。該函數(shù)將文件里BEIGIN后面的字符作為對象名保存在參數(shù)name里面;將BEGIN所在行和下一個空白行之間的所有信息都讀入到參數(shù)header里面,如果之間沒有信息,就將header設(shè)置為NULL;然后將信息體進行BASE64解碼放置到data參數(shù)里面,len是data參數(shù)的有效數(shù)據(jù)長度。該函數(shù)成功返回1,失敗返回0。

6.2 PEM_read_bio函數(shù)

該函數(shù)完成了跟PEM_read相同的功能,只不過讀取對象是BIO。事實上,PEM_read是通過調(diào)用本函數(shù)完成其功能的。該函數(shù)成功返回1,失敗返回0。

6.3 PEM_write函數(shù)

該函數(shù)將name參數(shù)的數(shù)據(jù)放在BEGIN頭的后面,寫入到fp文件;之后將參數(shù)hdr信息寫入到文件,并在后面寫入一個空白行;最后將data參數(shù)len字節(jié)的數(shù)據(jù)進行BASE64編碼,寫入到文件中,并最后加上END頭信息,返回PEM信息體的長度,失敗返回0。

6.4 PEM_write_bio函數(shù)

該函數(shù)跟PEM_write函數(shù)功能一樣,只是操作對象是BIO。事實上,PEM_write函數(shù)就是調(diào)用本函數(shù)完成其功能的。成功返回PEM信息體的長度,失敗返回0。

6.5 PEM_ASN1_read函數(shù)

該函數(shù)先調(diào)用PEM_read函數(shù)讀取PEM編碼的對象信息,然后調(diào)用PEM_get_EVP_CIPHER_INFO函數(shù)處理PEM格式中的DEK-info字段信息,以決定信息采用的加密算法和ivec值;加入PEM信息是加密了的,接下來就調(diào)用PEM_do_header函數(shù)解密信息體(參考第4部分),然后調(diào)用d2i函數(shù)將它進行DER解碼轉(zhuǎn)換成內(nèi)部定義個類型,保存在x參數(shù)中。成功返回指向x的指針,否則返回NULL。

注意,參數(shù)name必須是BEIGIN頭后面的PEM文件數(shù)據(jù)。因為函數(shù)調(diào)用了PEM_get_EVP_CIPHER_INFO函數(shù),所以為了函數(shù)能成功執(zhí)行,必須在調(diào)用本函數(shù)前加載算法。雖然事實上任何類型數(shù)據(jù)都可以進行加密,但一般來說只有RSA私鑰需要加密。本函數(shù)可以從一個文件中讀取一些列對象。

6.6 PEM_ASN1_read_bio函數(shù)

該函數(shù)功能跟PEM_ASN1_read函數(shù)一樣,不過操作對象是BIO。事實上,PEM_ASN1_read函數(shù)是調(diào)用本函數(shù)完成其功能的。成功返回指向x的指針,否則返回NULL。

6.7 PEM_ASN1_write函數(shù)

該函數(shù)將對象x使用i2d參數(shù)提供的函數(shù)轉(zhuǎn)換城DER編碼的數(shù)據(jù),接下來,如果enc參數(shù)不為NULL,就使用enc的加密算法加密這些數(shù)據(jù)。參數(shù)kstr是用來產(chǎn)生加密密鑰的,klen是kstr的有效長度。如果enc不是NULL,但是kstr是NULL,那么就會使用callback函數(shù)提示用戶輸入口令并獲取加密數(shù)據(jù);如果此時callback為NULL,但是u不為NULL,那么就是使用u作為產(chǎn)生加密密鑰的字符串,假定u應(yīng)該是NULL結(jié)束的字符串;如果callback和u都為NULL,那就會使用缺省的callback函數(shù)獲取口令。然后數(shù)據(jù)就被進行BASE64編碼寫入到fp文件中,加上BEIGIN開始頭信息、END結(jié)束頭信息、Type-Proc字段和DEK-info字段(如果數(shù)據(jù)被加密了)。加密密鑰在函數(shù)調(diào)用完之后就被清除了。成功操作返回1,否則返回0。

6.8 PEM_ASN1_wirte_bio函數(shù)

該函數(shù)實現(xiàn)的功能跟PEM_ASN1_write一樣,不過操作對象是BIO。事實上PEM_ASN1_write函數(shù)是調(diào)用本函數(shù)完成其功能的。成功操作返回1,否則返回0。

6.9 PEM_X509_INFO_read函數(shù)

該函數(shù)完成的功能跟PEM_ASN1_read是一樣的,除了它自動根據(jù)BEGIN頭信息調(diào)用了相應(yīng)的d2i系列函數(shù),目前支持的類型d2i_X509、d2i_X509_AUX、d2i_X509_CRL、d2i_RSAPrivateKey和d2i_DSAPrivateKey。該函數(shù)會對文件中的所有對象進行處理直到出錯或處理完畢。所有被處理好的對象都保存在堆棧sk中。因為有可能有些對象是加密的,所以提供了參數(shù)cb和u。參數(shù)cb和u的意義參照第3部分。成功返回處理好的堆棧指針,否則返回NULL。

6.10 PEM_X509_INFO_read_bio函數(shù)

該函數(shù)完成的功能跟PEM_X509_INFO_read函數(shù)一樣,除了操作對象是BIO之外。事實上,PEM_X509_INFO_read函數(shù)是調(diào)用本函數(shù)完成其功能的。成功返回處理好的堆棧指針,否則返回NULL。

6.11 PEM_X509_INFO_write_bio函數(shù)

該函數(shù)完成的功能也跟PEM_ASN1_write_bio一樣。除了它從參數(shù)xi中讀取每一部分對象,分別使用參數(shù)xi->x_pkey和xi->x509并使用相應(yīng)的i2d函數(shù)進行PEM編碼成獨立的信息,并寫入到bio中。同樣,可能要求用戶輸入口令生成加密密鑰,相關(guān)的參數(shù)cb、enc、kstr、klen以及u的意義參考前面的函數(shù)以及第3部分。該函數(shù)成功返回1,否則返回0。

7.PEM對象讀寫IO函數(shù)(一)

openssl基本上為其定義的每種對象都提供了用PEM格式進行讀寫的IO函數(shù)。在這種意義上說,PEM格式只是包含了頭信息的BASE64編碼的數(shù)據(jù)而已。這些函數(shù)基本上是基于第6部分所介紹的函數(shù)實現(xiàn)的,也就是說,他們多大部分只是這些函數(shù)的宏定義而已。因為我們在第3部分已經(jīng)詳細介紹了PEM系列函數(shù)的通用參數(shù),所以本文對這些通用參數(shù)不再作詳細的說明。

對于每個對象,openssl一般提供了四個函數(shù),比如名為Name的對象,提供的四個函數(shù)名就如下形式:

PEM_read_bio_Name()

PEM_read_Name()

PEM_write_bio_Name()

PEM_write_Name()

可以看到,有兩個是讀操作函數(shù),兩個是寫操作函數(shù)。其中,兩個讀操作函數(shù)或兩個寫操作函數(shù)都是功能相同的,不過就是對象一個為文件句柄,一個為BIO罷了。此外,所有對象的讀函數(shù)如果操作成功,返回相應(yīng)對象的指針,否則返回NULL;而寫函數(shù)則成功操作返回非0值,失敗返回0。下面我們對這些函數(shù)簡單分類介紹。

7.1 私鑰對象PrivateKey的IO

EVP_PKEY *PEM_read_bio_PrivateKey(BIO *bp, EVP_PKEY **x,pem_password_cb *cb, void *u);

EVP_PKEY *PEM_read_PrivateKey(FILE *fp, EVP_PKEY **x,pem_password_cb *cb, void *u);

int PEM_write_bio_PrivateKey(BIO *bp, EVP_PKEY *x, const EVP_CIPHER *enc,unsigned char *kstr, int klen,pem_password_cb *cb, void *u);

int PEM_write_PrivateKey(FILE *fp, EVP_PKEY *x, const EVP_CIPHER *enc,unsigned char *kstr, int klen,pem_password_cb *cb, void *u);

這些函數(shù)用PEM格式對一個EVP_PKEY結(jié)構(gòu)的私鑰進行讀寫操作。寫操作函數(shù)可以處理RSA或DSA類型的私鑰。讀操作函數(shù)還能透明的處理用PKCS#8格式加密和解密的私鑰。

1.往文件中寫入不加密的私鑰的例子

if (!PEM_write_PrivateKey(fp, key, NULL, NULL, 0, 0, NULL))

{

/* 錯誤處理代碼 */

}

2.往BIO中寫入一個私鑰,采用3DES加密,加密口令提示輸入的例子

if (!PEM_write_bio_PrivateKey(bp, key, EVP_des_ede3_cbc(), NULL, 0, 0, NULL))

{

/* 錯誤處理代碼 */

}

3.從BIO重讀取一個私鑰,使用”hello”作為解密口令的例子

key = PEM_read_bio_PrivateKey(bp, NULL, 0, “hello”);

if (key == NULL)

{

/* 錯誤處理代碼 */

}

4.從BIO中讀取一個私鑰,并使用回調(diào)函數(shù)獲得解密口令的例子

key = PEM_read_bio_PrivateKey(bp, NULL, pass_cb, “My Private Key”);

if (key == NULL)

{

/* 錯誤處理代碼 */

}

8.PEM對象讀寫IO函數(shù)(二)

本文繼續(xù)介紹PEM對象的讀寫IO函數(shù),請參看第7部分以便更好理解本文。

8.1符合PKCS#8和PKCS#5 v2.0標(biāo)準(zhǔn)的私鑰對象PKCS8PrivateKey的IO

int PEM_write_bio_PKCS8PrivateKey(BIO *bp, EVP_PKEY *x, const EVP_CIPHER *enc,char *kstr, int klen,pem_password_cb *cb, void *u);

int PEM_write_PKCS8PrivateKey(FILE *fp, EVP_PKEY *x, const EVP_CIPHER *enc,char *kstr, int klen,pem_password_cb *cb, void *u);

這兩個函數(shù)使用PKCS#8標(biāo)準(zhǔn)保存EVP_PKEY里面的私鑰到文件或者BIO中,并采用PKCS#5 v2.0的標(biāo)準(zhǔn)加密私鑰。enc參數(shù)定義了使用的加密算法。跟其他PEM的IO函數(shù)不一樣的是,本函數(shù)的加密是基于PKCS#8層次上的,而不是基于PEM信息字段的,所以這兩個函數(shù)也是單獨實現(xiàn)的函數(shù),而不是宏定義函數(shù)。如果enc參數(shù)為NULL,那么就不會執(zhí)行加密操作,只是使用PKCS#8私鑰信息結(jié)構(gòu)。成功執(zhí)行返回大于0 的數(shù),否則返回0。

使用這兩個函數(shù)保存的PEM對象可以使用上篇文章介紹的PEM_read_bio_PrivateKey或PEM_read_PrivateKey讀出來。

下面是一個將私鑰保存為PKCS#8格式,并使用3DES算法進行加密,使用的口令是”hello”的例子

if (!PEM_write_bio_PKCS8PrivateKey(bp, key, EVP_des_ede3_cbc(), NULL, 0, 0, “hello”))

{

/*出錯處理代碼*/

}

8.2符合PKCS#8和PKCS#5 v1.5或PKCS#12標(biāo)準(zhǔn)的私鑰對象PKCS8PrivateKey的IO

int PEM_write_bio_PKCS8PrivateKey_nid(BIO *bp, EVP_PKEY *x, int nid,char *kstr, int klen,pem_password_cb *cb, void *u);

int PEM_write_PKCS8PrivateKey_nid(FILE *fp, EVP_PKEY *x, int nid,char *kstr, int klen,pem_password_cb *cb, void *u);

這兩個函數(shù)也是單獨實現(xiàn)的函數(shù),而不是宏定義函數(shù)。他們也是將私鑰保存成PKCS#8格式,但是采用的方式是PKCS#5 v1.5或者PKCS#12進行私鑰的加密。nid參數(shù)指定了相應(yīng)的加密算法,其值應(yīng)該為相應(yīng)對象的NID。成功執(zhí)行返回大于0 的數(shù),否則返回0。

使用這兩個函數(shù)保存的PEM對象可以使用上篇文章介紹的PEM_read_bio_PrivateKey或PEM_read_PrivateKey讀出來。

8.3公鑰對象PUBKEY的IO

EVP_PKEY *PEM_read_bio_PUBKEY(BIO *bp, EVP_PKEY **x,pem_password_cb *cb, void *u);

EVP_PKEY *PEM_read_PUBKEY(FILE *fp, EVP_PKEY **x,pem_password_cb *cb, void *u);

int PEM_write_bio_PUBKEY(BIO *bp, EVP_PKEY *x);

int PEM_write_PUBKEY(FILE *fp, EVP_PKEY *x);

這四個函數(shù)對EVP_PKEY結(jié)構(gòu)的公鑰進行PEM格式的讀寫處理。公鑰是作為SubjectPublicKeyInfo存儲結(jié)構(gòu)進行編碼的。

8.4 RSA私鑰對象RSAPrivateKey的IO

RSA *PEM_read_bio_RSAPrivateKey(BIO *bp, RSA **x,pem_password_cb *cb, void *u);

RSA *PEM_read_RSAPrivateKey(FILE *fp, RSA **x,pem_password_cb *cb, void *u);

int PEM_write_bio_RSAPrivateKey(BIO *bp, RSA *x, const EVP_CIPHER *enc,unsigned char *kstr, int klen,pem_password_cb *cb, void *u);

int PEM_write_RSAPrivateKey(FILE *fp, RSA *x, const EVP_CIPHER *enc,unsigned char *kstr, int klen,pem_password_cb *cb, void *u);

這四個函數(shù)對RSA結(jié)構(gòu)的RSA私鑰進行PEM格式的讀寫處理。它使用跟PrivateKey相同的函數(shù)進行處理,但如果私鑰類型不是RSA,就會返回錯誤信息。

8.5 RSA公鑰對象RSAPublicKey的IO

RSA *PEM_read_bio_RSAPublicKey(BIO *bp, RSA **x,pem_password_cb *cb, void *u);

RSA *PEM_read_RSAPublicKey(FILE *fp, RSA **x,pem_password_cb *cb, void *u);

int PEM_write_bio_RSAPublicKey(BIO *bp, RSA *x);

int PEM_write_RSAPublicKey(FILE *fp, RSA *x);

這四個函數(shù)是對RSA結(jié)構(gòu)的公鑰進行PEM格式的讀寫處理。本函數(shù)使用PKCS#1 RSAPublicKey結(jié)構(gòu)標(biāo)準(zhǔn)對RSA公鑰進行編碼操作。

8.6 RSA公鑰對象RSA_PUBKEY的IO

RSA *PEM_read_bio_RSA_PUBKEY(BIO *bp, RSA **x,pem_password_cb *cb, void *u);

RSA *PEM_read_RSA_PUBKEY(FILE *fp, RSA **x,pem_password_cb *cb, void *u);

int PEM_write_bio_RSA_PUBKEY(BIO *bp, RSA *x);

int PEM_write_RSA_PUBKEY(FILE *fp, RSA *x);

這四個函數(shù)也是對RSA結(jié)構(gòu)的公鑰進行PEM格式的讀寫處理。但是本函數(shù)使用SubjectPublicKeyInfo結(jié)構(gòu)標(biāo)準(zhǔn)對RSA公鑰進行編碼操作,如果公鑰類型不是RSA,就出錯返回失敗信息。

9.PEM對象讀寫IO函數(shù)(三)

本文繼續(xù)介紹PEM對象的讀寫IO函數(shù),請參看第7部分和第8部分以便更好理解本文。

9.1 DSA私鑰對象DSAPrivateKey的IO函數(shù)

DSA *PEM_read_bio_DSAPrivateKey(BIO *bp, DSA **x,pem_password_cb *cb, void *u);

DSA *PEM_read_DSAPrivateKey(FILE *fp, DSA **x,pem_password_cb *cb, void *u);

int PEM_write_bio_DSAPrivateKey(BIO *bp, DSA *x, const EVP_CIPHER *enc,unsigned char *kstr, int klen,pem_password_cb *cb, void *u);

int PEM_write_DSAPrivateKey(FILE *fp, DSA *x, const EVP_CIPHER *enc,unsigned char *kstr, int klen,pem_password_cb *cb, void *u);

這些函數(shù)對以DSA結(jié)構(gòu)存儲的DSA私鑰進行PEM格式的IO讀寫。它們使用的處理格式跟PrivateKey系列函數(shù)是相同的,但是如果私鑰不是DSA類型的,則出錯返回。

9.2 DSA公鑰對象DSA_PUBKEY的IO函數(shù)

DSA *PEM_read_bio_DSA_PUBKEY(BIO *bp, DSA **x,pem_password_cb *cb, void *u);

DSA *PEM_read_DSA_PUBKEY(FILE *fp, DSA **x,pem_password_cb *cb, void *u);

int PEM_write_bio_DSA_PUBKEY(BIO *bp, DSA *x);

int PEM_write_DSA_PUBKEY(FILE *fp, DSA *x);

這些函數(shù)對以DSA結(jié)構(gòu)存儲的DSA公鑰進行PEM格式的IO讀寫。該公鑰是以SubjectPublicKeyInfo結(jié)構(gòu)進行編碼的,如果公鑰不是DSA類型,則將會出錯返回。

9.3 DSA參數(shù)對象DSAParams的IO函數(shù)

DSA *PEM_read_bio_DSAparams(BIO *bp, DSA **x, pem_password_cb *cb, void *u);

DSA *PEM_read_DSAparams(FILE *fp, DSA **x, pem_password_cb *cb, void *u);

int PEM_write_bio_DSAparams(BIO *bp, DSA *x);

int PEM_write_DSAparams(FILE *fp, DSA *x);

這些函數(shù)對以DSA結(jié)構(gòu)存儲的DSA參數(shù)進行PEM格式的IO讀寫操作。

9.4 DH參數(shù)對象DHParams的IO函數(shù)

DH *PEM_read_bio_DHparams(BIO *bp, DH **x, pem_password_cb *cb, void *u);

DH *PEM_read_DHparams(FILE *fp, DH **x, pem_password_cb *cb, void *u);

int PEM_write_bio_DHparams(BIO *bp, DH *x);

int PEM_write_DHparams(FILE *fp, DH *x);

這些函數(shù)對以DH結(jié)構(gòu)保存的DH參數(shù)進行PEM格式的IO讀寫操作,這些參數(shù)采用了PKCS#3的DH參數(shù)結(jié)構(gòu)進行編碼。

9.5 X509證書對象X509的IO函數(shù)

X509 *PEM_read_bio_X509(BIO *bp, X509 **x, pem_password_cb *cb, void *u);

X509 *PEM_read_X509(FILE *fp, X509 **x, pem_password_cb *cb, void *u);

int PEM_write_bio_X509(BIO *bp, X509 *x);

int PEM_write_X509(FILE *fp, X509 *x);

這些函數(shù)對以X509結(jié)構(gòu)保存的X509證書進行PEM格式的IO讀寫操作,這些函數(shù)也可以對信任X509證書進行相同的操作,但是信任設(shè)置信息會丟失。

9.6 X509信任證書對象X509_AUX的IO函數(shù)

X509 *PEM_read_bio_X509_AUX(BIO *bp, X509 **x, pem_password_cb *cb, void *u);

X509 *PEM_read_X509_AUX(FILE *fp, X509 **x, pem_password_cb *cb, void *u);

int PEM_write_bio_X509_AUX(BIO *bp, X509 *x);

int PEM_write_X509_AUX(FILE *fp, X509 *x);

這些函數(shù)對以X509結(jié)構(gòu)保存的信任X509證書進行PEM格式的IO讀寫操作。

9.7 X509證書請求對象X509_REQ的IO函數(shù)

X509_REQ *PEM_read_bio_X509_REQ(BIO *bp, X509_REQ **x,pem_password_cb *cb, void *u);

X509_REQ *PEM_read_X509_REQ(FILE *fp, X509_REQ **x,pem_password_cb *cb, void *u);

int PEM_write_bio_X509_REQ(BIO *bp, X509_REQ *x);

int PEM_write_X509_REQ(FILE *fp, X509_REQ *x);

int PEM_write_bio_X509_REQ_NEW(BIO *bp, X509_REQ *x);

int PEM_write_X509_REQ_NEW(FILE *fp, X509_REQ *x);

這些函數(shù)對以X509_REQ結(jié)構(gòu)存儲的符合PKCS#10標(biāo)準(zhǔn)的證書請求進行PEM格式的IO讀寫操作。不同的是,X509_REQ系列寫函數(shù)使用CERTIFICATE REQUEST作為頭,而X509_REQ_NEW系列寫函數(shù)則采用NEW CERTIFICATE REQUEST作為頭(一些CA要求這種格式)。而X509_REQ讀函數(shù)對這兩種情況都能處理,所以沒有X509_REQ_NEW的讀函數(shù)了。

9.8 X509吊銷列表對象X509_CRL的IO函數(shù)

X509_CRL *PEM_read_bio_X509_CRL(BIO *bp, X509_CRL **x,pem_password_cb *cb, void *u);

X509_CRL *PEM_read_X509_CRL(FILE *fp, X509_CRL **x,pem_password_cb *cb, void *u);

int PEM_write_bio_X509_CRL(BIO *bp, X509_CRL *x);

int PEM_write_X509_CRL(FILE *fp, X509_CRL *x);

這些函數(shù)對以X509_CRL結(jié)構(gòu)存儲的X509 CRL進行PEM格式的IO讀寫操作。

9.9 PKCS#7編碼內(nèi)容對象的PKCS7的IO函數(shù)

PKCS7 *PEM_read_bio_PKCS7(BIO *bp, PKCS7 **x, pem_password_cb *cb, void *u);

PKCS7 *PEM_read_PKCS7(FILE *fp, PKCS7 **x, pem_password_cb *cb, void *u);

int PEM_write_bio_PKCS7(BIO *bp, PKCS7 *x);

int PEM_write_PKCS7(FILE *fp, PKCS7 *x);

這些漢森對以PKCS7結(jié)構(gòu)存儲的PKCS#7內(nèi)容信息進行PEM格式的IO讀寫操作。

9.10 Netscape證書序列對象NETSCAPE_CERT_SEQUENCE的IO函數(shù)

NETSCAPE_CERT_SEQUENCE *PEM_read_bio_NETSCAPE_CERT_SEQUENCE(BIO *bp,NETSCAPE_CERT_SEQUENCE **x,pem_password_cb *cb, void *u);

NETSCAPE_CERT_SEQUENCE *PEM_read_NETSCAPE_CERT_SEQUENCE(FILE *fp,NETSCAPE_CERT_SEQUENCE **x,pem_password_cb *cb, void *u);

int PEM_write_bio_NETSCAPE_CERT_SEQUENCE(BIO *bp, NETSCAPE_CERT_SEQUENCE *x);

int PEM_write_NETSCAPE_CERT_SEQUENCE(FILE *fp, NETSCAPE_CERT_SEQUENCE *x);

這些函數(shù)對以NETSCAPE_CERT_SEQUENCE結(jié)構(gòu)存儲的Netscape證書序列進行PEM格式的IO讀寫操作。

10.回調(diào)函數(shù)例子
10.1獲取口令回調(diào)函數(shù)的例子

在PEM讀寫的過程中,特別對于私鑰文件,可能經(jīng)常要使用到獲取口令的回調(diào)函數(shù),在簽名我們介紹的一些列函數(shù)也可以看出,基本上都是帶有回調(diào)函數(shù)的參數(shù)的。openssl缺省的回調(diào)函數(shù)是基于命令行的,在許多情況下可能并不適應(yīng),這就要求用戶自己定義回調(diào)函數(shù)。在前面的相關(guān)章節(jié),我們已經(jīng)介紹了該回調(diào)函數(shù)的格式,現(xiàn)在我們給出一個回調(diào)函數(shù)的實現(xiàn)例子。

int pass_cb(char *buf, int size, int rwflag, void *u);

{

int len;

char *tmp;

/* rwflag是一個標(biāo)準(zhǔn),如果為1,可能還需要作些別的處理工作*/

printf(“輸入口令: \”%s\”\n”, u);

/* 這里應(yīng)該是得到口令的代碼*/

tmp = “hello”;

len = strlen(tmp);

if (len <= 0) return 0;

/* 如果口令超出給定長度,就把多余的刪掉 */

if (len > size) len = size;

memcpy(buf, tmp, len);

return len;

}

10.2使用PEM系列函數(shù)常犯的一個錯誤

PEM系列函數(shù)的格式和參數(shù)基本相同,下面是一個常犯的導(dǎo)致錯誤的用法。

X509 *x;

PEM_read_bio_X509(bp, &x, 0, NULL);

這樣的用法會導(dǎo)致出現(xiàn)不可預(yù)測的錯誤,因為x并沒有進行初始化,分配內(nèi)存空間,而接下來調(diào)用的函數(shù)卻會往x里面寫入數(shù)據(jù),導(dǎo)致內(nèi)存非法操作。這也是openssl本身沒有處理好的一個BUG.

Openssl之PEM系列

全球可信CA機構(gòu)

網(wǎng)頁名稱:Openssl之PEM系列
本文URL:http://www.muchs.cn/article44/gdgdee.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供電子商務(wù)、全網(wǎng)營銷推廣、App開發(fā)、動態(tài)網(wǎng)站、ChatGPT、服務(wù)器托管

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

h5響應(yīng)式網(wǎng)站建設(shè)