Openssl之PEM系列
創(chuàng)新互聯(lián)專注于如皋網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠(chéng)為您提供如皋營(yíng)銷型網(wǎng)站建設(shè),如皋網(wǎng)站制作、如皋網(wǎng)頁(yè)設(shè)計(jì)、如皋網(wǎng)站官網(wǎng)定制、小程序定制開發(fā)服務(wù),打造如皋網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供如皋網(wǎng)站排名全網(wǎng)營(yíng)銷落地服務(wù)。
PEM全稱是Privacy Enhanced Mail,該標(biāo)準(zhǔn)定義了加密一個(gè)準(zhǔn)備要發(fā)送郵件的標(biāo)準(zhǔn),主要用來(lái)將各種對(duì)象保存成PEM格式,并將PEM格式的各種對(duì)象讀取到相應(yīng)的結(jié)構(gòu)中。它的基本流程是這樣的:
Proc-Type,4:ENCRYPTED
DEK-Info: cipher-name, ivec其中,第一個(gè)頭信息標(biāo)注了該文件是否進(jìn)行了加密,該頭信息可能的值包括ENCRYPTED(信息已經(jīng)加密和簽名)、MIC-ONLY(信息經(jīng)過數(shù)字簽名但沒有加密)、MIC-CLEAR(信息經(jīng)過數(shù)字簽名但是沒有加密、也沒有進(jìn)行編碼,可使用非PEM格式閱讀)以及CLEAR(信息沒有簽名和加密并且沒有進(jìn)行編碼,該項(xiàng)好象是openssl自身的擴(kuò)展,但是并沒有真正實(shí)現(xiàn));;第二個(gè)頭信息標(biāo)注了加密的算法以及使用的ivec參量,ivec其實(shí)在這兒提供的應(yīng)該是一個(gè)隨機(jī)產(chǎn)生的數(shù)據(jù)序列,與塊加密算法中要使用到的初始化變量(IV)不一樣。
—–BEGIN PRIVACY-ENHANCED MESSAGE—–
在這些信息的后面加上如下形式尾標(biāo)注信息:
—–END PRIVACY-ENHANCED MESSAGE—–
上面是openssl的PEM文件的基本結(jié)構(gòu),需要注意的是,Openssl并沒有實(shí)現(xiàn)PEM的全部標(biāo)準(zhǔn),它只是對(duì)openssl中需要使用的一些選項(xiàng)做了實(shí)現(xiàn),詳細(xì)的PEM格式,請(qǐng)參考RFC1421-1424。
下面是一個(gè)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—–
有時(shí)候PEM編碼的東西并沒有經(jīng)過加密,只是簡(jiǎn)單進(jìn)行了BASE64編碼,下面是一個(gè)沒有加密的證書請(qǐng)求的例子:
—–BEGIN CERTIFICATE REQUEST—–
MIICVTCCAhMCAQAwUzELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEMMAoGA1UEAxMDUENBMIIBtTCCASkGBSsOAwIMMIIBHgKBgQCnP26Fv0FqKX3wn0cZMJCaCR3aajMexT2GlrMV4FMuj+BZgnOQPnUxmUd6UvuF5NmmezibaIqEm4fGHrV+hktTW1nPcWUZiG7OZq5riDb77Cjcwtelu+UsOSZL2ppwGJU3lRBWI/YV7boEXt45T/23Qx+1pGVvzYAR5HCVW1DNSQIVAPcHMe36bAYD1YWKHKycZedQZmVvAoGATd9MA6aRivUZb1BGJZnlaG8w42nh6bNdmLsohkj83pkEP1+IDJxzJA0gXbkqmj8YlifkYofBe3RiU/xhJ6h7kQmdtvFNnFQPWAbuSXQHzlV+I84W9srcWmEBfslxtU323DQph3j2XiCTs9v15AlsQReVkusBtXOlan7YMu0OArgDgYUAAoGBAKbtuR5AdW+ICjCFe2ixjUiJJzM2IKwe6NZEMXg39+HQ1UTPTmfLZLps+rZfolHDXuRKMXbGFdSF0nXYzotPCzi7GauwEJTZyr27ZZjA1C6apGSQ9GzuwNvZ4rCXystVEagAS8OQ4H3D4dWS17Zg31ICb5o4E5r0z09o/Uz46u0VoAAwCQYFKw4DAhsFAAMxADAuAhUArRubTxsbIXy3AhtjQ943AbNBnSICFQCu+g1iW3jwF+gOcbroD4S/ZcvB3w==
—–END CERTIFICATE REQUEST—–
可以看到,該文件沒有了前面兩個(gè)頭信息。大家如果經(jīng)常使用openssl的應(yīng)用程序,就對(duì)這些文件格式很熟悉了。
openssl中定義的PEM相關(guān)結(jié)構(gòu)體如下(openssl\pem.h),這些結(jié)構(gòu)體是所有PEM系列函數(shù)的基礎(chǔ)。
下面定義的是PEM一個(gè)高層應(yīng)用結(jié)構(gòu),該結(jié)構(gòu)通過PEM_SealInit進(jìn)行初始化,最后使用PEM_SealFinal進(jìn)行釋放,該結(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中的一個(gè)子結(jié)構(gòu),用來(lái)保存用戶的信息
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)的定義,我們將在注釋里面對(duì)必要的參數(shù)進(jìn)行說明。
typedef struct pem_ctx_st
{
int type;//結(jié)構(gòu)類型
struct
{
int version;//版本號(hào)
int mode;//編碼方式
} proc_type;//Proc_Type字段信息,包括版本號(hào)和編碼方式
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; //信息摘要算法是否進(jìn)行了加密(簽名)
int md_len; //摘要信息的長(zhǎng)度
char *md_data; //摘要信息,可以是經(jīng)過了加密(簽名)的信息
EVP_CIPHER *dec;//數(shù)據(jù)加密算法
int key_len; //密鑰長(zhǎng)度
unsigned char *key; //加密密鑰
int data_enc; //數(shù)據(jù)是否加密標(biāo)志
int data_len; //數(shù)據(jù)長(zhǎng)度
unsigned char *data; //數(shù)據(jù)
} PEM_CTX;
下面我們對(duì)PEM_CTX結(jié)構(gòu)體中一些重要的參數(shù)做詳細(xì)的說明
該參數(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)
該參數(shù)是保存了PEM標(biāo)準(zhǔn)中Proc_Type字段的信息(參考《openssl之PEM系列之1》),可以看到,該結(jié)構(gòu)包括兩個(gè)字段,第一個(gè)字段version是版本號(hào),第二個(gè)字段mode是信息的編碼方式,目前定義了四種,如下:
#define PEM_TYPE_ENCRYPTED 10
#define PEM_TYPE_MIC_ONLY 20
#define PEM_TYPE_MIC_CLEAR 30
#define PEM_TYPE_CLEAR 40
這四個(gè)值的意義可以參考《openssl之PEM系列之1》。值得注意是,在openssl實(shí)現(xiàn)的PEM文件中,最后一個(gè)PEM_TYPE_CLEAR其實(shí)并沒有用到。
該參數(shù)定義了PEM中DEK_info字段的信息,本來(lái)該參數(shù)應(yīng)該含有兩個(gè)字段,包括加密算法和IV。但是由于歷史原因,openssl中原有的非標(biāo)準(zhǔn)的IV字段在新版的openssl中取消了,所以就剩下一個(gè)算法定義了,目前支持的算法如下述的定義:
#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
PEM系列函數(shù)中很多參數(shù)是相同意義的,也就是說通用的。本節(jié)將對(duì)這些通用參數(shù)的意義進(jìn)行介紹,以便于后述章節(jié)能夠更方便流暢地進(jìn)行PEM系列函數(shù)的介紹。
如果函數(shù)有該參數(shù),則定義了進(jìn)行數(shù)據(jù)讀寫B(tài)IO接口。
如果函數(shù)包含了該參數(shù),則定義了進(jìn)行數(shù)據(jù)讀寫的FILE指針。
PEM讀操作的系列函數(shù)都有TYPE **x 和返回TYEP *指針的參數(shù)。這里的TYPE可以為任何函數(shù)要使用的結(jié)構(gòu)體,如DSA或X509之類的。如果參數(shù)x是NULL,那么該參數(shù)將被忽略。如果x不是NULL,但是*x是NULL,那么返回的結(jié)構(gòu)體就會(huì)寫入到*x里面。如果x和*x都不是NULL,那么函數(shù)就試圖重用*x中的結(jié)構(gòu)體。這中函數(shù)總是返回一個(gè)執(zhí)行結(jié)構(gòu)體的指針(x的值),如果出錯(cuò),就返回NULL。
enc參數(shù)定義了PEM函數(shù)寫私鑰的時(shí)候采用的加密算法。加密是在PEM層進(jìn)行的。如果該參數(shù)為NULL,那么私鑰就會(huì)以不加密的形式寫入相應(yīng)的接口。
cb參數(shù)定義了回調(diào)函數(shù),該回調(diào)函數(shù)在加密PEM結(jié)構(gòu)體(一般來(lái)說是私鑰)需要口令的時(shí)候使用。
主要在PEM寫系列函數(shù)里面使用,如果該參數(shù)不為NULL,那么kstr中klen字節(jié)數(shù)據(jù)就用來(lái)作為口令,此時(shí),cb參數(shù)就被忽略了。
如果cb參數(shù)為NULL,而u參數(shù)不為NULL,那么u參數(shù)就是一個(gè)以NULL結(jié)束的字符串用作口令。如果cb和u參數(shù)都是NULL,那么缺省的回調(diào)函數(shù)就會(huì)并使用,該函數(shù)一般在當(dāng)前的終端提示輸入口令,并且關(guān)掉了回顯功能。
因?yàn)槿笔〉幕卣{(diào)函數(shù)基于終端的,有時(shí)候不適合使用(如GUI程序),所以可以使用替換的回調(diào)函數(shù)?;氐胶瘮?shù)的形式如下:
int cb(char *buf, int size, int rwflag, void *u);
在該函數(shù)中,buf是保存口令的參數(shù)。size是考慮最大的長(zhǎng)度(如buf的長(zhǎng)度)。rwflag是一個(gè)讀寫標(biāo)志,0的時(shí)候?yàn)樽x操作,1的時(shí)候?yàn)閷懖僮?。?dāng)rwflag為1的時(shí)候,典型的函數(shù)一般會(huì)要求用戶驗(yàn)證口令(如輸入兩次)。u參數(shù)跟上述PEM函數(shù)的u參數(shù)意義是一樣的,它允許應(yīng)用程序使用固定的數(shù)據(jù)作為參數(shù)傳給回調(diào)函數(shù)。回調(diào)函數(shù)必須返回口令字符的數(shù)目,如果出錯(cuò)返回0。
本次介紹的函數(shù)是處理PEM結(jié)構(gòu)里面一些字段信息的函數(shù),這些函數(shù)在一般應(yīng)用中可能不會(huì)用到,但是深入一點(diǎn)的應(yīng)用,恐怕就避免不了。此外,了解這些應(yīng)用,對(duì)于加深對(duì)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);
該函數(shù)是通過給定參數(shù)type返回一個(gè)標(biāo)準(zhǔn)的PEM文件的Proc-Type字段信息。返回的信息寫入到buf參數(shù)里面去,所以要求buf分配的內(nèi)存空間必須足夠大。事實(shí)上,該函數(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為其它值時(shí),返回字符串為 “Proc-Type: 4,BAD-TYPE\n”
事實(shí)上,雖然上字段信息中有MIC(信息摘要)選項(xiàng),但openssl的PEM庫(kù)并沒有實(shí)現(xiàn)MIC計(jì)算的功能。當(dāng)然,可以通過使用RSA-MD系列函數(shù)將PEM的數(shù)據(jù)信息進(jìn)行摘要并將該結(jié)果作為PEM的MIC。你可以通過PEM_dek_info函數(shù)產(chǎn)生MIC-info頭信息,然后寫入到PEM結(jié)構(gòu)中,不過據(jù)openssl的說明,這需要的時(shí)間可能會(huì)比較長(zhǎng),大概5分鐘左右。
該函數(shù)跟上述函數(shù)相似,是根據(jù)type參數(shù)生成DEK-info字段的信息,返回并寫入到buf里面。參數(shù)str里應(yīng)該是提供了ivec變量的值,參數(shù)len是str的長(zhǎng)度(單位是字節(jié))。在這里,參數(shù)type應(yīng)該為加密算法的名字,原則上這個(gè)字符串可以是任意的,但是為了其它程序能夠正確解釋該字段,你可以先得到算法相應(yīng)的NID,然后通過調(diào)用nid2sn得到該算法的簡(jiǎn)稱作為type參數(shù)。例如我們需要在PEM_ASN1_write_bio中使用算法結(jié)構(gòu)enc,那么可以調(diào)用下面函數(shù):
objstr=OBJ_nid2sn(EVP_CIPHER_nid(enc));
此時(shí)objstr就是一個(gè)包含了算法enc的簡(jiǎn)稱的字符串。然后我們就可以通過下面的語(yǔ)句在PEM_dek_info函數(shù)中使用這個(gè)字符串了:
PEM_dek_info(buf,objstr,8,(char *)iv);
該函數(shù)并非顧名思義,事實(shí)上它完成了對(duì)一個(gè)PEM編碼對(duì)象的的解密工作(如果該P(yáng)EM對(duì)象需要進(jìn)行解密),該函數(shù)通常是被PEM_read_bio所調(diào)用的。在調(diào)用該函數(shù)之前,應(yīng)該已經(jīng)將PEM文件的一些頭信息得到,以便于正確進(jìn)行解密操作。其中,DEK-info字段的信息應(yīng)該在調(diào)用本函數(shù)之前進(jìn)行正確的處理,從而通過該字段的名字和ivec得到相應(yīng)的EVP_CIPHER結(jié)構(gòu)信息和IV變量,作為本函數(shù)的cipher參數(shù)。
如果PEM文件沒有DEK-info字段,那么該函數(shù)簡(jiǎn)單返回1,操作成功,因?yàn)椴恍枰M(jìn)行解密操作。如果不是的話,那么該函數(shù)就需要一個(gè)口令來(lái)進(jìn)行解密。首先,它會(huì)試圖從callback參數(shù)(一個(gè)回調(diào)函數(shù))中得到該口令?;卣{(diào)函數(shù)的格式如下:
callback(buffer, blen, verify)
其中,參數(shù)buffer是保存返回口令的地方,blen是buffer的最大長(zhǎng)度,verify參數(shù)是指明是否需要口令驗(yàn)證(就是要求用戶輸入兩次相同的口令),默認(rèn)的是0。
如果callback參數(shù)為NULL,而u參數(shù)不為NULL,那么u參數(shù)就會(huì)以NULL為結(jié)束符的字符串作為口令寫入到buffer中;如果callback和u參數(shù)都為NULL,那么就會(huì)調(diào)用缺省的callback函數(shù)(關(guān)于u的具體意義,請(qǐng)參考《openssl之PEM系列之3》)。PEM_do_header函數(shù)得到口令后,就使用該口令(包括長(zhǎng)度信息)跟cipher參數(shù)種的ivec變量一起對(duì)數(shù)據(jù)進(jìn)行解密。解密后的數(shù)據(jù)保存在data中,長(zhǎng)度信息保存在plen中。該函數(shù)操作成功返回1,否則返回0。
該函數(shù)一般也被PEM_read_bio函數(shù)調(diào)用。在調(diào)用該函數(shù)之前,PEM的Proc-Type頭信息應(yīng)該已經(jīng)作為明文被讀入到header參數(shù)中。如果header為NULL,那么函數(shù)成功返回1,因?yàn)闆]有什么頭信息要處理。如果不為NULL,那么該函數(shù)首先確定header信息是否以“Proc-Type:4,ENCRYPTED”開頭,如果是其它形式的,該函數(shù)將返回0,不進(jìn)行處理。之后,函數(shù)開始讀取DEK-info字段的信息,然后函數(shù)通過該字段的加密算法名字使用EVP_get_cihperbyname得到一個(gè)EVP_CIPHER結(jié)構(gòu),并保存在參數(shù)cipher->cipher中;然后函數(shù)再通過調(diào)用內(nèi)部的函數(shù)得到ivec的值,并保存在cipher->iv中。成功操作返回1,否則返回0。
需要注意的是,因?yàn)樵摵瘮?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,從而將所有加密算法的信息載入到程序中。具體的情況請(qǐng)參考《openssl之EVP系列》相關(guān)章節(jié)。
該系列函數(shù)完成了對(duì)PEM對(duì)象以及相關(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ù)完成了對(duì)PEM對(duì)象、密鑰和IV變量的加密編碼工作,PEM_Sign系列函數(shù)完成了對(duì)PEM進(jìn)行數(shù)字簽名的工作。
該函數(shù)為后續(xù)的PEM_SealUpdate和PEM_SealFinal函數(shù)做初始化工作。首先,該函數(shù)使用參數(shù)md_type調(diào)用函數(shù)EVP_SignInit對(duì)信息摘要結(jié)構(gòu)ctx->md進(jìn)行初始化。然后,該函數(shù)通過參數(shù)type找到相應(yīng)的EVP_CIPHER結(jié)構(gòu),產(chǎn)生適用于該算法的密鑰和ivec變量并保存在該算法結(jié)構(gòu)中,然后使用參數(shù)pubk的公鑰調(diào)用函數(shù)EVP_SealInit對(duì)該密鑰進(jìn)行加密。加密后的秘鑰保存在參數(shù)ek里面,其長(zhǎng)度保存在ekl里面,這些數(shù)據(jù)都是調(diào)用了EVP_EncodeUpdate函數(shù)經(jīng)過了BASE64編碼的。因?yàn)槊荑€和IV已經(jīng)保存在ctx->cipher中,所以,可以被后續(xù)的函數(shù)用來(lái)對(duì)PEM對(duì)象進(jìn)行加密處理。該函數(shù)成功操作返回正值,否則返回0或-1。
需要注意的是,因?yàn)楸竞瘮?shù)也使用了加密算法名字查找算法結(jié)構(gòu),所以在調(diào)用本函數(shù)之前必須加載該靜態(tài)算法結(jié)構(gòu)棧。
該函數(shù)用來(lái)完成對(duì)PEM對(duì)象信息體的加密和編碼,使用的加密密鑰是PEM_SealInit函數(shù)產(chǎn)生的。該函數(shù)對(duì)參數(shù)in中的inl個(gè)字節(jié)的數(shù)據(jù)采用ctx->cipher提供的對(duì)稱加密算法結(jié)構(gòu)(已經(jīng)包含了密鑰和IV)進(jìn)行加密操作,然后調(diào)用EVP_EncodeUpdate進(jìn)行BASE64編碼后保存在參數(shù)out里面,outl是out里有效數(shù)據(jù)的長(zhǎng)度信息。在此同時(shí),該函數(shù)也調(diào)用函數(shù)EVP_SignUpdate函數(shù)使用ctx->md的摘要算法結(jié)構(gòu)對(duì)參數(shù)in里的數(shù)據(jù)進(jìn)行了信息摘要操作,不過暫時(shí)沒有輸出,等調(diào)用了PEM_SealFinal函數(shù)的時(shí)候進(jìn)行輸出。
需要注意的是,該函對(duì)輸入的信息in的長(zhǎng)度做了限制,不能大于1200字節(jié),否則將超過1200字節(jié)的信息簡(jiǎn)單丟棄。
該函數(shù)完成整個(gè)PEM_Seal系列的操作。首先,它完成了之前使用PEM_SealUpdate函數(shù)進(jìn)行處理的數(shù)據(jù)的對(duì)稱加密工作,將數(shù)據(jù)進(jìn)行BASE64編碼并輸出到參數(shù)out,outl保存了out數(shù)據(jù)的有效長(zhǎng)度。同時(shí),該函數(shù)還完成了信息摘要工作,并使用參數(shù)priv的私鑰對(duì)該信息進(jìn)行簽名(加密),將結(jié)果經(jīng)過BASE64編碼后輸出到參數(shù)sig,sigl是sig有效數(shù)據(jù)的長(zhǎng)度信息。該函數(shù)成功操作返回1,否則返回0。
需要注意的是,該函數(shù)運(yùn)行完后,就將ctx->md和ctx->cipher結(jié)構(gòu)釋放清除掉了,所以如果你想保存對(duì)稱加密算法使用的密鑰和IV的話,你需要在調(diào)用本函數(shù)之前就保存一個(gè)備份。當(dāng)然,一般情況下是不會(huì)這么做的,因?yàn)檫@些密鑰應(yīng)該是臨時(shí)密鑰,只用來(lái)加密一個(gè)信息。
完成上述三個(gè)函數(shù)的操作之后,你就得到了加密后的密鑰、IV(從PEM_SealInit函數(shù))以及PEM對(duì)象信息體,并且這些都是經(jīng)過BASE64編碼的。然后,你就可以將這些信息發(fā)送給接受方了。對(duì)方接受到這些信息后,使用他自己的私鑰以及你的公鑰,就能進(jìn)行正確的數(shù)據(jù)解密和驗(yàn)證。
這三個(gè)函數(shù)完成的功能跟EVP_Sign系列函數(shù)是一樣的,其實(shí),前面兩個(gè)函數(shù)就簡(jiǎn)單調(diào)用了EVP_SignInit和EVP_SignUpdate函數(shù)。PEM_SignFinal則調(diào)用EVP_SignFinal函數(shù)完成信息摘要和簽名(使用參數(shù)pkey的私鑰)之后,調(diào)用了EVP_EncodeBlock對(duì)簽名信息進(jìn)行了BASE64編碼,然后將編碼后的簽名信息保存在參數(shù)sigret,siglen保存了sigret有效數(shù)據(jù)的長(zhǎng)度。PEM_SignFinal函數(shù)成功返回1,否則返回0。
該函數(shù)使用了PEM庫(kù)的錯(cuò)誤代碼信息對(duì)錯(cuò)誤處理庫(kù)進(jìn)行初始化,必須在使用任何PEM系列函數(shù)之前調(diào)用該函數(shù)。
PEM提供了一系列底層的進(jìn)行數(shù)據(jù)讀寫操作的IO函數(shù),在后面章節(jié)敘述到的PEM對(duì)象的IO函數(shù)都是這些函數(shù)的宏定義,所以雖然一般不要直接調(diào)用這些函數(shù),做一個(gè)清楚的了解還是必要的。這些函數(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部分介紹過,在此將不再詳細(xì)介紹。
該函數(shù)從文件fp里面讀取一個(gè)PEM編碼的信息。該函數(shù)將文件里BEIGIN后面的字符作為對(duì)象名保存在參數(shù)name里面;將BEGIN所在行和下一個(gè)空白行之間的所有信息都讀入到參數(shù)header里面,如果之間沒有信息,就將header設(shè)置為NULL;然后將信息體進(jìn)行BASE64解碼放置到data參數(shù)里面,len是data參數(shù)的有效數(shù)據(jù)長(zhǎng)度。該函數(shù)成功返回1,失敗返回0。
該函數(shù)完成了跟PEM_read相同的功能,只不過讀取對(duì)象是BIO。事實(shí)上,PEM_read是通過調(diào)用本函數(shù)完成其功能的。該函數(shù)成功返回1,失敗返回0。
該函數(shù)將name參數(shù)的數(shù)據(jù)放在BEGIN頭的后面,寫入到fp文件;之后將參數(shù)hdr信息寫入到文件,并在后面寫入一個(gè)空白行;最后將data參數(shù)len字節(jié)的數(shù)據(jù)進(jìn)行BASE64編碼,寫入到文件中,并最后加上END頭信息,返回PEM信息體的長(zhǎng)度,失敗返回0。
該函數(shù)跟PEM_write函數(shù)功能一樣,只是操作對(duì)象是BIO。事實(shí)上,PEM_write函數(shù)就是調(diào)用本函數(shù)完成其功能的。成功返回PEM信息體的長(zhǎng)度,失敗返回0。
該函數(shù)先調(diào)用PEM_read函數(shù)讀取PEM編碼的對(duì)象信息,然后調(diào)用PEM_get_EVP_CIPHER_INFO函數(shù)處理PEM格式中的DEK-info字段信息,以決定信息采用的加密算法和ivec值;加入PEM信息是加密了的,接下來(lái)就調(diào)用PEM_do_header函數(shù)解密信息體(參考第4部分),然后調(diào)用d2i函數(shù)將它進(jìn)行DER解碼轉(zhuǎn)換成內(nèi)部定義個(gè)類型,保存在x參數(shù)中。成功返回指向x的指針,否則返回NULL。
注意,參數(shù)name必須是BEIGIN頭后面的PEM文件數(shù)據(jù)。因?yàn)楹瘮?shù)調(diào)用了PEM_get_EVP_CIPHER_INFO函數(shù),所以為了函數(shù)能成功執(zhí)行,必須在調(diào)用本函數(shù)前加載算法。雖然事實(shí)上任何類型數(shù)據(jù)都可以進(jìn)行加密,但一般來(lái)說只有RSA私鑰需要加密。本函數(shù)可以從一個(gè)文件中讀取一些列對(duì)象。
該函數(shù)功能跟PEM_ASN1_read函數(shù)一樣,不過操作對(duì)象是BIO。事實(shí)上,PEM_ASN1_read函數(shù)是調(diào)用本函數(shù)完成其功能的。成功返回指向x的指針,否則返回NULL。
該函數(shù)將對(duì)象x使用i2d參數(shù)提供的函數(shù)轉(zhuǎn)換城DER編碼的數(shù)據(jù),接下來(lái),如果enc參數(shù)不為NULL,就使用enc的加密算法加密這些數(shù)據(jù)。參數(shù)kstr是用來(lái)產(chǎn)生加密密鑰的,klen是kstr的有效長(zhǎng)度。如果enc不是NULL,但是kstr是NULL,那么就會(huì)使用callback函數(shù)提示用戶輸入口令并獲取加密數(shù)據(jù);如果此時(shí)callback為NULL,但是u不為NULL,那么就是使用u作為產(chǎn)生加密密鑰的字符串,假定u應(yīng)該是NULL結(jié)束的字符串;如果callback和u都為NULL,那就會(huì)使用缺省的callback函數(shù)獲取口令。然后數(shù)據(jù)就被進(jìn)行BASE64編碼寫入到fp文件中,加上BEIGIN開始頭信息、END結(jié)束頭信息、Type-Proc字段和DEK-info字段(如果數(shù)據(jù)被加密了)。加密密鑰在函數(shù)調(diào)用完之后就被清除了。成功操作返回1,否則返回0。
該函數(shù)實(shí)現(xiàn)的功能跟PEM_ASN1_write一樣,不過操作對(duì)象是BIO。事實(shí)上PEM_ASN1_write函數(shù)是調(diào)用本函數(shù)完成其功能的。成功操作返回1,否則返回0。
該函數(shù)完成的功能跟PEM_ASN1_read是一樣的,除了它自動(dòng)根據(jù)BEGIN頭信息調(diào)用了相應(yīng)的d2i系列函數(shù),目前支持的類型d2i_X509、d2i_X509_AUX、d2i_X509_CRL、d2i_RSAPrivateKey和d2i_DSAPrivateKey。該函數(shù)會(huì)對(duì)文件中的所有對(duì)象進(jìn)行處理直到出錯(cuò)或處理完畢。所有被處理好的對(duì)象都保存在堆棧sk中。因?yàn)橛锌赡苡行?duì)象是加密的,所以提供了參數(shù)cb和u。參數(shù)cb和u的意義參照第3部分。成功返回處理好的堆棧指針,否則返回NULL。
該函數(shù)完成的功能跟PEM_X509_INFO_read函數(shù)一樣,除了操作對(duì)象是BIO之外。事實(shí)上,PEM_X509_INFO_read函數(shù)是調(diào)用本函數(shù)完成其功能的。成功返回處理好的堆棧指針,否則返回NULL。
該函數(shù)完成的功能也跟PEM_ASN1_write_bio一樣。除了它從參數(shù)xi中讀取每一部分對(duì)象,分別使用參數(shù)xi->x_pkey和xi->x509并使用相應(yīng)的i2d函數(shù)進(jìn)行PEM編碼成獨(dú)立的信息,并寫入到bio中。同樣,可能要求用戶輸入口令生成加密密鑰,相關(guān)的參數(shù)cb、enc、kstr、klen以及u的意義參考前面的函數(shù)以及第3部分。該函數(shù)成功返回1,否則返回0。
openssl基本上為其定義的每種對(duì)象都提供了用PEM格式進(jìn)行讀寫的IO函數(shù)。在這種意義上說,PEM格式只是包含了頭信息的BASE64編碼的數(shù)據(jù)而已。這些函數(shù)基本上是基于第6部分所介紹的函數(shù)實(shí)現(xiàn)的,也就是說,他們多大部分只是這些函數(shù)的宏定義而已。因?yàn)槲覀冊(cè)诘?部分已經(jīng)詳細(xì)介紹了PEM系列函數(shù)的通用參數(shù),所以本文對(duì)這些通用參數(shù)不再作詳細(xì)的說明。
對(duì)于每個(gè)對(duì)象,openssl一般提供了四個(gè)函數(shù),比如名為Name的對(duì)象,提供的四個(gè)函數(shù)名就如下形式:
PEM_read_bio_Name()
PEM_read_Name()
PEM_write_bio_Name()
PEM_write_Name()
可以看到,有兩個(gè)是讀操作函數(shù),兩個(gè)是寫操作函數(shù)。其中,兩個(gè)讀操作函數(shù)或兩個(gè)寫操作函數(shù)都是功能相同的,不過就是對(duì)象一個(gè)為文件句柄,一個(gè)為BIO罷了。此外,所有對(duì)象的讀函數(shù)如果操作成功,返回相應(yīng)對(duì)象的指針,否則返回NULL;而寫函數(shù)則成功操作返回非0值,失敗返回0。下面我們對(duì)這些函數(shù)簡(jiǎn)單分類介紹。
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格式對(duì)一個(gè)EVP_PKEY結(jié)構(gòu)的私鑰進(jìn)行讀寫操作。寫操作函數(shù)可以處理RSA或DSA類型的私鑰。讀操作函數(shù)還能透明的處理用PKCS#8格式加密和解密的私鑰。
1.往文件中寫入不加密的私鑰的例子
if (!PEM_write_PrivateKey(fp, key, NULL, NULL, 0, 0, NULL))
{
/* 錯(cuò)誤處理代碼 */
}
2.往BIO中寫入一個(gè)私鑰,采用3DES加密,加密口令提示輸入的例子
if (!PEM_write_bio_PrivateKey(bp, key, EVP_des_ede3_cbc(), NULL, 0, 0, NULL))
{
/* 錯(cuò)誤處理代碼 */
}
3.從BIO重讀取一個(gè)私鑰,使用”hello”作為解密口令的例子
key = PEM_read_bio_PrivateKey(bp, NULL, 0, “hello”);
if (key == NULL)
{
/* 錯(cuò)誤處理代碼 */
}
4.從BIO中讀取一個(gè)私鑰,并使用回調(diào)函數(shù)獲得解密口令的例子
key = PEM_read_bio_PrivateKey(bp, NULL, pass_cb, “My Private Key”);
if (key == NULL)
{
/* 錯(cuò)誤處理代碼 */
}
本文繼續(xù)介紹PEM對(duì)象的讀寫IO函數(shù),請(qǐng)參看第7部分以便更好理解本文。
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);
這兩個(gè)函數(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信息字段的,所以這兩個(gè)函數(shù)也是單獨(dú)實(shí)現(xiàn)的函數(shù),而不是宏定義函數(shù)。如果enc參數(shù)為NULL,那么就不會(huì)執(zhí)行加密操作,只是使用PKCS#8私鑰信息結(jié)構(gòu)。成功執(zhí)行返回大于0 的數(shù),否則返回0。
使用這兩個(gè)函數(shù)保存的PEM對(duì)象可以使用上篇文章介紹的PEM_read_bio_PrivateKey或PEM_read_PrivateKey讀出來(lái)。
下面是一個(gè)將私鑰保存為PKCS#8格式,并使用3DES算法進(jìn)行加密,使用的口令是”hello”的例子
if (!PEM_write_bio_PKCS8PrivateKey(bp, key, EVP_des_ede3_cbc(), NULL, 0, 0, “hello”))
{
/*出錯(cuò)處理代碼*/
}
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);
這兩個(gè)函數(shù)也是單獨(dú)實(shí)現(xiàn)的函數(shù),而不是宏定義函數(shù)。他們也是將私鑰保存成PKCS#8格式,但是采用的方式是PKCS#5 v1.5或者PKCS#12進(jìn)行私鑰的加密。nid參數(shù)指定了相應(yīng)的加密算法,其值應(yīng)該為相應(yīng)對(duì)象的NID。成功執(zhí)行返回大于0 的數(shù),否則返回0。
使用這兩個(gè)函數(shù)保存的PEM對(duì)象可以使用上篇文章介紹的PEM_read_bio_PrivateKey或PEM_read_PrivateKey讀出來(lái)。
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);
這四個(gè)函數(shù)對(duì)EVP_PKEY結(jié)構(gòu)的公鑰進(jìn)行PEM格式的讀寫處理。公鑰是作為SubjectPublicKeyInfo存儲(chǔ)結(jié)構(gòu)進(jìn)行編碼的。
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);
這四個(gè)函數(shù)對(duì)RSA結(jié)構(gòu)的RSA私鑰進(jìn)行PEM格式的讀寫處理。它使用跟PrivateKey相同的函數(shù)進(jìn)行處理,但如果私鑰類型不是RSA,就會(huì)返回錯(cuò)誤信息。
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);
這四個(gè)函數(shù)是對(duì)RSA結(jié)構(gòu)的公鑰進(jìn)行PEM格式的讀寫處理。本函數(shù)使用PKCS#1 RSAPublicKey結(jié)構(gòu)標(biāo)準(zhǔn)對(duì)RSA公鑰進(jìn)行編碼操作。
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);
這四個(gè)函數(shù)也是對(duì)RSA結(jié)構(gòu)的公鑰進(jìn)行PEM格式的讀寫處理。但是本函數(shù)使用SubjectPublicKeyInfo結(jié)構(gòu)標(biāo)準(zhǔn)對(duì)RSA公鑰進(jìn)行編碼操作,如果公鑰類型不是RSA,就出錯(cuò)返回失敗信息。
本文繼續(xù)介紹PEM對(duì)象的讀寫IO函數(shù),請(qǐng)參看第7部分和第8部分以便更好理解本文。
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ù)對(duì)以DSA結(jié)構(gòu)存儲(chǔ)的DSA私鑰進(jìn)行PEM格式的IO讀寫。它們使用的處理格式跟PrivateKey系列函數(shù)是相同的,但是如果私鑰不是DSA類型的,則出錯(cuò)返回。
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ù)對(duì)以DSA結(jié)構(gòu)存儲(chǔ)的DSA公鑰進(jìn)行PEM格式的IO讀寫。該公鑰是以SubjectPublicKeyInfo結(jié)構(gòu)進(jìn)行編碼的,如果公鑰不是DSA類型,則將會(huì)出錯(cuò)返回。
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ù)對(duì)以DSA結(jié)構(gòu)存儲(chǔ)的DSA參數(shù)進(jìn)行PEM格式的IO讀寫操作。
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ù)對(duì)以DH結(jié)構(gòu)保存的DH參數(shù)進(jìn)行PEM格式的IO讀寫操作,這些參數(shù)采用了PKCS#3的DH參數(shù)結(jié)構(gòu)進(jìn)行編碼。
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ù)對(duì)以X509結(jié)構(gòu)保存的X509證書進(jìn)行PEM格式的IO讀寫操作,這些函數(shù)也可以對(duì)信任X509證書進(jìn)行相同的操作,但是信任設(shè)置信息會(huì)丟失。
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ù)對(duì)以X509結(jié)構(gòu)保存的信任X509證書進(jìn)行PEM格式的IO讀寫操作。
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ù)對(duì)以X509_REQ結(jié)構(gòu)存儲(chǔ)的符合PKCS#10標(biāo)準(zhǔn)的證書請(qǐng)求進(jìn)行PEM格式的IO讀寫操作。不同的是,X509_REQ系列寫函數(shù)使用CERTIFICATE REQUEST作為頭,而X509_REQ_NEW系列寫函數(shù)則采用NEW CERTIFICATE REQUEST作為頭(一些CA要求這種格式)。而X509_REQ讀函數(shù)對(duì)這兩種情況都能處理,所以沒有X509_REQ_NEW的讀函數(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ù)對(duì)以X509_CRL結(jié)構(gòu)存儲(chǔ)的X509 CRL進(jìn)行PEM格式的IO讀寫操作。
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);
這些漢森對(duì)以PKCS7結(jié)構(gòu)存儲(chǔ)的PKCS#7內(nèi)容信息進(jìn)行PEM格式的IO讀寫操作。
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ù)對(duì)以NETSCAPE_CERT_SEQUENCE結(jié)構(gòu)存儲(chǔ)的Netscape證書序列進(jìn)行PEM格式的IO讀寫操作。
在PEM讀寫的過程中,特別對(duì)于私鑰文件,可能經(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)在我們給出一個(gè)回調(diào)函數(shù)的實(shí)現(xiàn)例子。
int pass_cb(char *buf, int size, int rwflag, void *u);
{
int len;
char *tmp;
/* rwflag是一個(gè)標(biāo)準(zhǔn),如果為1,可能還需要作些別的處理工作*/
printf(“輸入口令: \”%s\”\n”, u);
/* 這里應(yīng)該是得到口令的代碼*/
tmp = “hello”;
len = strlen(tmp);
if (len <= 0) return 0;
/* 如果口令超出給定長(zhǎng)度,就把多余的刪掉 */
if (len > size) len = size;
memcpy(buf, tmp, len);
return len;
}
PEM系列函數(shù)的格式和參數(shù)基本相同,下面是一個(gè)常犯的導(dǎo)致錯(cuò)誤的用法。
X509 *x;
PEM_read_bio_X509(bp, &x, 0, NULL);
這樣的用法會(huì)導(dǎo)致出現(xiàn)不可預(yù)測(cè)的錯(cuò)誤,因?yàn)閤并沒有進(jìn)行初始化,分配內(nèi)存空間,而接下來(lái)調(diào)用的函數(shù)卻會(huì)往x里面寫入數(shù)據(jù),導(dǎo)致內(nèi)存非法操作。這也是openssl本身沒有處理好的一個(gè)BUG.
全球可信CA機(jī)構(gòu)
網(wǎng)頁(yè)名稱:Openssl之PEM系列
本文URL:http://www.rwnh.cn/article44/gdgdee.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供電子商務(wù)、全網(wǎng)營(yíng)銷推廣、App開發(fā)、動(dòng)態(tài)網(wǎng)站、ChatGPT、服務(wù)器托管
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)