OpenSSL中文手册之PEM库详解

  版权声明本文根据DragonKing牛,E-Mail:wzhah@263.NET发布在https://openssl.126.com的系列文章整理修改而成(这个网站已经不能访问了),我自己所做的工作主要是针对新的1.0.2版本进行验证,修改错别字,和错误,重新排版,以及整理分类,配图。 未经作者允许,严禁用于商业出版,否则追究法律责任。网络转载请注明出处,这是对原创者的起码的尊重!!!


1 PEM编码文件结构介绍

  PEM全称是Privacy Enhanced Mail,该标准定义了加密一个准备要发送邮件的标准,主要用来将各种对象保存成PEM格式,并将PEM格式的各种对象读取到相应的结构中。它的基本流程是这样的:

  • 信息转换为ASCII码或其它编码方式;
  • 使用对称算法加密转换了的邮件信息;
  • 使用BASE64对加密后的邮件信息进行编码;
  • 使用一些头定义对信息进行封装,这些头信息格式如下(不一定都需要,可选的):

        `Proc-Type,4:ENCRYPTED`
        `DEK-Info: cipher-name, ivec`
    
    • 第一个头信息标注了该文件是否进行了加密,该头信息可能的值包括:
      • ENCRYPTED——信息已经加密和签名
      • MIC-ONLY——信息经过数字签名但没有加密
      • MIC-CLEAR——信息经过数字签名但是没有加密、也没有进行编码,可使用非PEM格式阅读
      • CLEAR——信息没有签名和加密并且没有进行编码,该项好象是openssl自身的扩展,但是并没有真正实现
    • 第二个头信息标注了加密的算法以及使用的ivec参量,ivec其实在这儿提供的应该是一个随机产生的数据序列,与块加密算法中要使用到的初始化变量(IV)不一样
  • 在这些信息的前面加上如下形式头标注信息:

    —–BEGIN PRIVACY-ENHANCED MESSAGE—–
    
  • 在这些信息的后面加上如下形式尾标注信息:

    —–END PRIVACY-ENHANCED MESSAGE—–
    

  上面是openssl的PEM文件的基本结构,需要注意的是,Openssl并没有实现PEM的全部标准,它只是对openssl中需要使用的一些选项做了实现,详细的PEM格式,请参考RFC1421-1424。

  下面是一个PEM编码的经过加密的DSA私钥的例子:

—–BEGIN DSA PRIVATE KEY—–

Proc-Type: 4,ENCRYPTED

DEK-Info: DES-EDE3-CBC,F80EEEBEEA7386C4

GZ9zgFcHOlnhPoiSbVi/yXc9mGoj44A6IveD4UlpSEUt6Xbse3Fr0KHIUyQ3oGnSm
ClKoAp/eOTb5Frhto85SzdsxYtac+X1v5XwdzAMy2KowHVk1N8A5jmE2OlkNPNtof
132MNlo2cyIRYaa35PPYBGNCmUm7YcYS8O90YtkrQZZTf4+2C4kllhMcdkQwkrFWS
WC8YOQ7w0LHb4cX1FejHHom9Nd/0PN3vn3UyySvfOqoR7nbXkrpHXmPIr0hxXRcF0
aXcV/CzZ1/nfXWQf4o3+oD0T22SDoVcZY60IzI0oIc3pNCbDV3uKNmgekrFdqOUJ+
QW8oWp7oefRx62iBfIeC8DZunohMXaWAQCU0sLQOR4yEdeUCnzCSywe0bG1diD0KY
aEe+Yub1BQH4aLsBgDjardgpJRTQLq0DUvw0/QGO1irKTJzegEDNVBKrVnV4AHOKT
1CUKqvGNRP1UnccUDTF6miOAtaj/qpzra7sSk7dkGBvIEeFoAg84kfh9hhVvF1Yyz
C9bwZepruoqoUwke/WdNIR5ymOVZ/4Liw0JdIOcq+atbdRX08niqIRkfdsZrUj4le
o3zdefYUQ7w4N2Ns37yDFq7

—–END DSA PRIVATE KEY—–

  有时候PEM编码的东西并没有经过加密,只是简单进行了BASE64编码,下面是一个没有加密的证书请求的例子:

—–BEGIN CERTIFICATE REQUEST—–

MIICVTCCAhMCAQAwUzELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxI
TAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEMMAoGA1UEAxMDUENBMI
IBtTCCASkGBSsOAwIMMIIBHgKBgQCnP26Fv0FqKX3wn0cZMJCaCR3aajMexT2GlrM
V4FMuj+BZgnOQPnUxmUd6UvuF5NmmezibaIqEm4fGHrV+hktTW1nPcWUZiG7OZq5r
iDb77Cjcwtelu+UsOSZL2ppwGJU3lRBWI/YV7boEXt45T/23Qx+1pGVvzYAR5HCVW
1DNSQIVAPcHMe36bAYD1YWKHKycZedQZmVvAoGATd9MA6aRivUZb1BGJZnlaG8w42
nh5bNdmLsohkj83pkEP1+IDJxzJA0gXbkqmj8YlifkYofBe3RiU/xhJ6h6kQmdtvF
NnFQPWAbuSXQHzlV+I84W9srcWmEBfslxtU323DQph2j2XiCTs9v15AlsQReVkusB
tXOlan7YMu0OArgDgYUAAoGBAKbtuR5AdW+ICjCFe2ixjUiJJzM2IKwe6NZEMXg39
+HQ1UTPTmfLZLps+rZfolHDXuRKMXbGFdSF0nXYzotPCzi7GauwEJTZyr27ZZjA1C
6apGSQ9GzuwNvZ4rCXystVEagAS8OQ4H3D4dWS17Zg31ICb5o4E5r0z09o/Uz46u0
VoAAwCQYFKw4DAhsFAAMxADAuAhUArRubTxsbIXy3AhtjQ943AbNBnSICFQCu+g1i
W3jwF+gOcbroD4S/ZcvB3w==

—–END CERTIFICATE REQUEST—–

可以看到,该文件没有了前面两个头信息。大家如果经常使用openssl的应用程序,就对这些文件格式很熟悉了。

2 PEM类型和实现结构介绍

  openssl中定义的PEM相关结构体如下(openssl\pem.h),这些结构体是所有PEM系列函数的基础。
  下面定义的是PEM一个高层应用结构,该结构通过PEM_SealInit进行初始化,最后使用PEM_SealFinal进行释放,该结构定义了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中的一个子结构,用来保存用户的信息

typedef struct pem_recip_st
{
    char *name;
    X509_NAME *dn;
    int cipher;
    int key_enc;
} PEM_USER;

  下面是PEM主结构体PEM_CTX结构的定义,我们将在注释里面对必要的参数进行说明。

typedef struct pem_ctx_st
{
    int type;              //结构类型

    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;        //摘要信息,可以是经过了加密(签名)的信息    
    EVP_CIPHER *dec;      //数据加密算法  
    int key_len;          //密钥长度    
    unsigned char *key;   //加密密钥    
    int data_enc;         //数据是否加密标志    
    int data_len;         //数据长度
    unsigned char *data;  //数据 
} PEM_CTX;

  下面我们对PEM_CTX结构体中一些重要的参数做详细的说明

  • int type参数——该参数指明了PEM_CTX结构的类型,目前包括了以下定义的类型:
#define PEM_OBJ_UNDEF             0   //未定义
#define PEM_OBJ_X509              1   //x509证书
#define PEM_OBJ_X509_REQ          2   //x509证书请求
#define PEM_OBJ_CRL               3   //吊销
#define PEM_OBJ_SSL_SESSION       4   //ssl会话
#define PEM_OBJ_PRIV_KEY          10  //私钥
#define PEM_OBJ_PRIV_RSA          11  //RSA私钥
#define PEM_OBJ_PRIV_DSA          12  //DSA私钥
#define PEM_OBJ_PRIV_DH           13  //DH私钥
#define PEM_OBJ_PUB_RSA           14  //RSA公钥
#define PEM_OBJ_PUB_DSA           15  //DSA公钥
#define PEM_OBJ_PUB_DH            16  //DH公钥
#define PEM_OBJ_DHPARAMS          17  //DH参数
#define PEM_OBJ_DSAPARAMS         18  //DSA公钥
#define PEM_OBJ_PRIV_RSA_PUBLIC   19   //RSA私钥证书

  可以看到,这些类型基本上包括了所有openssl中要使用的基本结构

  • struct proc_type参数——该参数是保存了PEM标准中Proc_Type字段的信息,可以看到,该结构包括两个字段,第一个字段version是版本号,第二个字段mode是信息的编码方式,目前定义了四种,如下:
#define PEM_TYPE_ENCRYPTED  10 //信息已经加密和签名
#define PEM_TYPE_MIC_ONLY   20 //信息经过数字签名但没有加密
#define PEM_TYPE_MIC_CLEAR  30 //信息经过数字签名但是没有加密、没有进行编码,可使用非PEM格式阅读
#define PEM_TYPE_CLEAR      40 //信息没有签名和加密并且没有进行编码,好象是openssl自身的扩展,但并没有真正实现

  值得注意是,在openssl实现的PEM文件中,最后一个PEM_TYPE_CLEAR其实并没有用到。

  • struct DEK_info参数——该参数定义了PEM中DEK_info字段的信息,本来该参数应该含有两个字段,包括加密算法和IV。但是由于历史原因,openssl中原有的非标准的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 函数概览

#include <openssl/pem.h>
//------------------------------------------------------------------------------
//私钥 读写
EVP_PKEY *PEM_read_bio_PrivateKey(BIO *bp, 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);

EVP_PKEY *PEM_read_PrivateKey(FILE *fp, EVP_PKEY **x,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);


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_bi
  • 14
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝月心语

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值