




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領
文檔簡介
1、.Openssl之PEM系列作者: LaoKa200804261.PEM編碼文件結(jié)構(gòu)介紹PEM全稱是Privacy Enhanced Mail,該標準定義了加密一個準備要發(fā)送郵件的標準,主要用來將各種對象保存成PEM格式,并將PEM格式的各種對象讀取到相應的結(jié)構(gòu)中。它的基本流程是這樣的:1. 信息轉(zhuǎn)換為ASCII碼或其它編碼方式;2. 使用對稱算法加密轉(zhuǎn)換了的郵件信息;3. 使用BASE64對加密后的郵件信息進行編碼;4. 使用一些頭定義對信息進行封裝,這些頭信息格式如下(不一定都需要,可選的):Proc-Type,4:ENCRYPTED DEK-Info: cipher-name, ivec
2、其中,第一個頭信息標注了該文件是否進行了加密,該頭信息可能的值包括ENCRYPTED(信息已經(jīng)加密和簽名)、MIC-ONLY(信息經(jīng)過數(shù)字簽名但沒有加密)、MIC-CLEAR(信息經(jīng)過數(shù)字簽名但是沒有加密、也沒有進行編碼,可使用非PEM格式閱讀)以及CLEAR(信息沒有簽名和加密并且沒有進行編碼,該項好象是openssl自身的擴展,但是并沒有真正實現(xiàn));第二個頭信息標注了加密的算法以及使用的ivec參量,ivec其實在這兒提供的應該是一個隨機產(chǎn)生的數(shù)據(jù)序列,與塊加密算法中要使用到的初始化變量(IV)不一樣。5. 在這些信息的前面加上如下形式頭標注信息:-BEGIN PRIVACY-ENHANC
3、ED MESSAGE-在這些信息的后面加上如下形式尾標注信息:-END PRIVACY-ENHANCED MESSAGE-上面是openssl的PEM文件的基本結(jié)構(gòu),需要注意的是,Openssl并沒有實現(xiàn)PEM的全部標準,它只是對openssl中需要使用的一些選項做了實現(xiàn),詳細的PEM格式,請參考RFC14211424。下面是一個PEM編碼的經(jīng)過加密的DSA私鑰的例子:-BEGIN DSA PRIVATE KEY-Proc-Type: 4,ENCRYPTEDDEK-Info: DES-EDE3-CBC,F80EEEBEEA7386C4GZ9zgFcHOlnhPoiSbVi/yXc9mGoj44
4、A6IveD4UlpSEUt6Xbse3Fr0KHIUyQ3oGnSmClKoAp/eOTb5Frhto85SzdsxYtac+X1v5XwdzAMy2KowHVk1N8A5jmE2OlkNPNtof132MNlo2cyIRYaa35PPYBGNCmUm7YcYS8O90YtkrQZZTf4+2C4kllhMcdkQwkrFWSWC8YOQ7w0LHb4cX1FejHHom9Nd/0PN3vn3UyySvfOqoR7nbXkrpHXmPIr0hxXRcF0aXcV/CzZ1/nfXWQf4o3+oD0T22SDoVcZY60IzI0oIc3pNCbDV3uKNmgekrFdqOUJ+QW8oW
5、p7oefRx62iBfIeC8DZunohMXaWAQCU0sLQOR4yEdeUCnzCSywe0bG1diD0KYaEe+Yub1BQH4aLsBgDjardgpJRTQLq0DUvw0/QGO1irKTJzegEDNVBKrVnV4AHOKT1CUKqvGNRP1UnccUDTF6miOAtaj/qpzra7sSk7dkGBvIEeFoAg84kfh9hhVvF1YyzC9bwZepruoqoUwke/WdNIR5ymOVZ/4Liw0JdIOcq+atbdRX08niqIRkfdsZrUj4leo3zdefYUQ7w4N2Ns37yDFq7-END DSA PRIVATE KEY-有
6、時候PEM編碼的東西并沒有經(jīng)過加密,只是簡單進行了BASE64編碼,下面是一個沒有加密的證書請求的例子:-BEGIN CERTIFICATE REQUEST-MIICVTCCAhMCAQAwUzELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEMMAoGA1UEAxMDUENBMIIBtTCCASkGBSsOAwIMMIIBHgKBgQCnP26Fv0FqKX3wn0cZMJCaCR3aajMexT2GlrMV4FMuj+BZgnOQPnUxmUd6UvuF5
7、NmmezibaIqEm4fGHrV+hktTW1nPcWUZiG7OZq5riDb77Cjcwtelu+UsOSZL2ppwGJU3lRBWI/YV7boEXt45T/23Qx+1pGVvzYAR5HCVW1DNSQIVAPcHMe36bAYD1YWKHKycZedQZmVvAoGATd9MA6aRivUZb1BGJZnlaG8w42nh5bNdmLsohkj83pkEP1+IDJxzJA0gXbkqmj8YlifkYofBe3RiU/xhJ6h6kQmdtvFNnFQPWAbuSXQHzlV+I84W9srcWmEBfslxtU323DQph2j2XiCTs9v15AlsQReVkusBt
8、XOlan7YMu0OArgDgYUAAoGBAKbtuR5AdW+ICjCFe2ixjUiJJzM2IKwe6NZEMXg39+HQ1UTPTmfLZLps+rZfolHDXuRKMXbGFdSF0nXYzotPCzi7GauwEJTZyr27ZZjA1C6apGSQ9GzuwNvZ4rCXystVEagAS8OQ4H3D4dWS17Zg31ICb5o4E5r0z09o/Uz46u0VoAAwCQYFKw4DAhsFAAMxADAuAhUArRubTxsbIXy3AhtjQ943AbNBnSICFQCu+g1iW3jwF+gOcbroD4S/ZcvB3w=-END CERTIFICATE R
9、EQUEST-可以看到,該文件沒有了前面兩個頭信息。大家如果經(jīng)常使用openssl的應用程序,就對這些文件格式很熟悉了。2.PEM類型和實現(xiàn)結(jié)構(gòu)介紹openssl中定義的PEM相關結(jié)構(gòu)體如下(opensslpem.h),這些結(jié)構(gòu)體是所有PEM系列函數(shù)的基礎。下面定義的是PEM一個高層應用結(jié)構(gòu),該結(jié)構(gòu)通過PEM_SealInit進行初始化,最后使用PEM_SealFinal進行釋放,該結(jié)構(gòu)定義了PEM中要使用的編碼算法、信息摘要算法以及加密算法。typedef struct PEM_Encode_Seal_stEVP_ENCODE_CTX encode;EVP_MD_CTX md;EVP_CIP
10、HER_CTX cipher; PEM_ENCODE_SEAL_CTX;下面定義了PEM_CTX中的一個子結(jié)構(gòu),用來保存用戶的信息typedef struct pem_recip_stchar *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_stint type;/結(jié)構(gòu)類型structint version;/版本號int mode;/編碼方式 proc_type;/Proc_Type字段信息,包括版本號和編
11、碼方式char *domain;structint cipher; DEK_info;/定義了PEM中DEK_info字段的信息PEM_USER *originator;int num_recipient;PEM_USER *recipient;#ifndef OPENSSL_NO_STACKSTACK *x509_chain;/保存證書鏈#elsechar *x509_chain; /保存證書鏈#endifEVP_MD *md; /簽名算法類型,指定了信息摘要算法和簽名算法int md_enc; /信息摘要算法是否進行了加密(簽名)int md_len; /摘要信息的長度char *md_d
12、ata; /摘要信息,可以是經(jīng)過了加密(簽名)的信息EVP_CIPHER *dec;/數(shù)據(jù)加密算法int key_len; /密鑰長度unsigned char *key; /加密密鑰int data_enc; /數(shù)據(jù)是否加密標志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_O
13、BJ_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
14、18#define PEM_OBJ_PRIV_RSA_PUBLIC 19可以看到,這些類型基本上包括了所有openssl中要使用的基本結(jié)構(gòu)2.2 struct proc_type參數(shù)該參數(shù)是保存了PEM標準中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_TYP
15、E_CLEAR 40這四個值的意義可以參考openssl之PEM系列之1。值得注意是,在openssl實現(xiàn)的PEM文件中,最后一個PEM_TYPE_CLEAR其實并沒有用到。2.3 struct DEK_info參數(shù)該參數(shù)定義了PEM中DEK_info字段的信息,本來該參數(shù)應該含有兩個字段,包括加密算法和IV。但是由于歷史原因,openssl中原有的非標準的IV字段在新版的openssl中取消了,所以就剩下一個算法定義了,目前支持的算法如下述的定義:#define PEM_DEK_DES_CBC 40#define PEM_DEK_IDEA_CBC 45#define PEM_DEK_DES_
16、EDE 50#define PEM_DEK_DES_ECB 60#define PEM_DEK_RSA 70#define PEM_DEK_RSA_MD2 80#define PEM_DEK_RSA_MD5 903.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 和返回
17、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,那么私鑰就會以不加密的形式寫入相應的接口。3.5 cb參數(shù)cb參數(shù)定義了回調(diào)函數(shù),該回調(diào)函數(shù)在加密PEM結(jié)構(gòu)體(一般來說是私鑰)需要口
18、令的時候使用。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ù)一般在當前的終端提示輸入口令,并且關掉了回顯功能。3.8回調(diào)函數(shù)callback函數(shù)介紹因為缺省的回調(diào)函數(shù)基于終端的,有時候不適合使用(如GUI程序),所以可以使用替換的回調(diào)函數(shù)。回到函數(shù)的形式如下:int cb(char *buf, int size, int
19、 rwflag, void *u);在該函數(shù)中,buf是保存口令的參數(shù)。size是考慮最大的長度(如buf的長度)。rwflag是一個讀寫標志,0的時候為讀操作,1的時候為寫操作。當rwflag為1的時候,典型的函數(shù)一般會要求用戶驗證口令(如輸入兩次)。u參數(shù)跟上述PEM函數(shù)的u參數(shù)意義是一樣的,它允許應用程序使用固定的數(shù)據(jù)作為參數(shù)傳給回調(diào)函數(shù)。回調(diào)函數(shù)必須返回口令字符的數(shù)目,如果出錯返回0。4.PEM結(jié)構(gòu)信息處理函數(shù)本次介紹的函數(shù)是處理PEM結(jié)構(gòu)里面一些字段信息的函數(shù),這些函數(shù)在一般應用中可能不會用到,但是深入一點的應用,恐怕就避免不了。此外,了解這些應用,對于加深對PEM結(jié)構(gòu)的理解也是很有
20、好處的。下面是其中相關一些函數(shù)的定義(opensslpem.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
21、len, char *str);4.1 PEM_proc_type該函數(shù)是通過給定參數(shù)type返回一個標準的PEM文件的Proc-Type字段信息。返回的信息寫入到buf參數(shù)里面去,所以要求buf分配的內(nèi)存空間必須足夠大。事實上,該函數(shù)返回的字符串不外乎下面四種結(jié)果:當type為PEM_TYPE_ENCRYPTED,返回字符串為"Proc-Type: 4,ENCRYPTEDn"當type為PEM_TYPE_MIC_CLEAR,返回字符串為"Proc-Type: 4,MIC-CLEARn"當type為PEM_TYPE_MIC_ONLY,返回字符串為&quo
22、t;Proc-Type: 4,MIC-ONLYn"當type為其它值時,返回字符串為 "Proc-Type: 4,BAD-TYPEn"事實上,雖然上字段信息中有MIC(信息摘要)選項,但openssl的PEM庫并沒有實現(xiàn)MIC計算的功能。當然,可以通過使用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ù)生成D
23、EK-info字段的信息,返回并寫入到buf里面。參數(shù)str里應該是提供了ivec變量的值,參數(shù)len是str的長度(單位是字節(jié))。在這里,參數(shù)type應該為加密算法的名字,原則上這個字符串可以是任意的,但是為了其它程序能夠正確解釋該字段,你可以先得到算法相應的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_
24、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ù)之前,應該已經(jīng)將PEM文件的一些頭信息得到,以便于正確進行解密操作。其中,DEK-info字段的信息應該在調(diào)用本函數(shù)之前進行正確的處理,從而通過該字段的名字和ivec得到相應的EVP_CIPHER結(jié)構(gòu)信息和IV變量,作為本函數(shù)的cipher參數(shù)。如果PEM文件沒有DEK-info字段,那么該函數(shù)簡
25、單返回1,操作成功,因為不需要進行解密操作。如果不是的話,那么該函數(shù)就需要一個口令來進行解密。首先,它會試圖從callback參數(shù)(一個回調(diào)函數(shù))中得到該口令。回調(diào)函數(shù)的格式如下:callback(buffer, blen, verify)其中,參數(shù)buffer是保存返回口令的地方,blen是buffer的最大長度,verify參數(shù)是指明是否需要口令驗證(就是要求用戶輸入兩次相同的口令),默認的是0。如果callback參數(shù)為NULL,而u參數(shù)不為NULL,那么u參數(shù)就會以NULL為結(jié)束符的字符串作為口令寫入到buffer中;如果callback和u參數(shù)都為NULL,那么就會調(diào)用缺省的call
26、back函數(shù)(關于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頭信息應該已經(jīng)作為明文被讀入到header參數(shù)中。如果header為NULL,那么函數(shù)成功返回1,因為沒有什么頭信息要處理。如果不為NULL,那么該函數(shù)首先確定head
27、er信息是否以“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ù)前,應該先調(diào)用EVP_add_cipher和EVP_add_alias,或者調(diào)用S
28、SLeay_add_all_algorithms,從而將所有加密算法的信息載入到程序中。具體的情況請參考openssl之EVP系列相關章節(jié)。5.PEM信息封裝加密系列函數(shù)該系列函數(shù)完成了對PEM對象以及相關密鑰和IV向量的加密編碼工作,以便于數(shù)據(jù)的保存和傳送,主要包括以下函數(shù)(opensslpem.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 npu
29、bk);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
30、_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ù)做初始化
31、工作。首先,該函數(shù)使用參數(shù)md_type調(diào)用函數(shù)EVP_SignInit對信息摘要結(jié)構(gòu)ctx->md進行初始化。然后,該函數(shù)通過參數(shù)type找到相應的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。
32、需要注意的是,因為本函數(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
33、里的數(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是si
34、g有效數(shù)據(jù)的長度信息。該函數(shù)成功操作返回1,否則返回0。需要注意的是,該函數(shù)運行完后,就將ctx->md和ctx->cipher結(jié)構(gòu)釋放清除掉了,所以如果你想保存對稱加密算法使用的密鑰和IV的話,你需要在調(diào)用本函數(shù)之前就保存一個備份。當然,一般情況下是不會這么做的,因為這些密鑰應該是臨時密鑰,只用來加密一個信息。5.4 PEM_Seal操作總結(jié)完成上述三個函數(shù)的操作之后,你就得到了加密后的密鑰、IV(從PEM_SealInit函數(shù))以及PEM對象信息體,并且這些都是經(jīng)過BASE64編碼的。然后,你就可以將這些信息發(fā)送給接受方了。對方接受到這些信息后,使用他自己的私鑰以及你的公鑰,就
35、能進行正確的數(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.
36、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ù)定義如下(opensslpem.h):int PEM_read_bio(BIO *bp, char *name, char *header,unsigned char *data,long *len);int PEM_write_bio(BIO
37、*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_
38、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_
39、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_p
40、assword_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
41、部分介紹過,在此將不再詳細介紹。6.1 PEM_read函數(shù)該函數(shù)從文件fp里面讀取一個PEM編碼的信息。該函數(shù)將文件里BEIGIN后面的字符作為對象名保存在參數(shù)name里面;將BEGIN所在行和下一個空白行之間的所有信息都讀入到參數(shù)header里面,如果之間沒有信息,就將header設置為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,失
42、敗返回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
43、_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ù)可以從一
44、個文件中讀取一些列對象。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ù);如果此
45、時callback為NULL,但是u不為NULL,那么就是使用u作為產(chǎn)生加密密鑰的字符串,假定u應該是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)用
46、本函數(shù)完成其功能的。成功操作返回1,否則返回0。6.9 PEM_X509_INFO_read函數(shù)該函數(shù)完成的功能跟PEM_ASN1_read是一樣的,除了它自動根據(jù)BEGIN頭信息調(diào)用了相應的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
47、_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并使用相應的i2d函數(shù)進行PEM編碼成獨立的信息,并寫入到bio中。同樣,可能要求用戶輸入口令生成加密密鑰,相關的參數(shù)cb、enc
48、、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
49、()PEM_read_Name()PEM_write_bio_Name()PEM_write_Name()可以看到,有兩個是讀操作函數(shù),兩個是寫操作函數(shù)。其中,兩個讀操作函數(shù)或兩個寫操作函數(shù)都是功能相同的,不過就是對象一個為文件句柄,一個為BIO罷了。此外,所有對象的讀函數(shù)如果操作成功,返回相應對象的指針,否則返回NULL;而寫函數(shù)則成功操作返回非0值,失敗返回0。下面我們對這些函數(shù)簡單分類介紹。7.1 私鑰對象PrivateKey的IOEVP_PKEY *PEM_read_bio_PrivateKey(BIO *bp, EVP_PKEY *x,pem_password_cb *cb, voi
50、d *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
51、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_
52、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)/*
53、 錯誤處理代碼 */8.PEM對象讀寫IO函數(shù)(二)本文繼續(xù)介紹PEM對象的讀寫IO函數(shù),請參看第7部分以便更好理解本文。8.1符合PKCS#8和PKCS#5 v2.0標準的私鑰對象PKCS8PrivateKey的IOint 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
54、_CIPHER *enc,char *kstr, int klen,pem_password_cb *cb, void *u);這兩個函數(shù)使用PKCS#8標準保存EVP_PKEY里面的私鑰到文件或者BIO中,并采用PKCS#5 v2.0的標準加密私鑰。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對象可以使用上
55、篇文章介紹的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標準的私鑰對象PKCS8PrivateKey的IOint PEM_write_bio_PKCS8Priv
56、ateKey_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ù)指定了相應的加密
57、算法,其值應該為相應對象的NID。成功執(zhí)行返回大于0 的數(shù),否則返回0。使用這兩個函數(shù)保存的PEM對象可以使用上篇文章介紹的PEM_read_bio_PrivateKey或PEM_read_PrivateKey讀出來。8.3公鑰對象PUBKEY的IOEVP_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_bi
58、o_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的IORSA *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 *
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 金屬餐具的供應鏈管理優(yōu)化考核試卷
- 紡織行業(yè)的經(jīng)濟價值考核試卷
- 計算機網(wǎng)絡設計與實施相關試題及答案
- 公路施工決策分析試題及答案
- 數(shù)據(jù)庫安全策略與用戶管理試題及答案
- 鉆探設備在寶石礦勘查中的技術(shù)要求考核試卷
- 液體乳品物流與供應鏈優(yōu)化策略考核試卷
- 計算機三級考試中心知識回顧與試題及答案
- 計算機在多媒體信息處理與內(nèi)容分發(fā)考核試卷
- 行政管理理論基礎知識試題及答案
- 職業(yè)道德與法治 第13課《學會依法維權(quán)》第二框課件《崇尚程序正義》
- mm-pbsa計算原理結(jié)果
- 國家開放大學《中文學科論文寫作》形考任務1-4參考答案
- 【真題】2023年常州市中考道德與法治試卷(含答案解析)
- 酒吧計劃創(chuàng)業(yè)計劃書
- 光伏項目安全培訓課件
- 拉森鋼板樁監(jiān)理實施細則樣本
- 個人房屋抵押借款合同范本-借款合同
- 《原碼一位乘法》課件
- 中華人民共和國監(jiān)察法學習解讀課件
- 中小學教務主任培訓
評論
0/150
提交評論