欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Openssl解析私pfx/p12证书(1)

程序员文章站 2022-07-12 21:55:40
...

   PKCS#12标准描述了个人标识信息的语法,一种交换数字证书的加密标准,包括用户公钥、私钥、证书等。Openssl提供了API供我们解析pfx/p12文件,提取我们需要的信息。

   首先我们需要了解几个数据结构,由于Openssl文档里面有些介绍的不是很详细,在这里列举一下:

   1、X509 struct

typedef struct x509_st X509;
struct x509_st
{
X509_CINF *cert_info;//证书主体信息
X509_ALGOR *sig_alg;//签名算法信息
ASN1_BIT_STRING *signature;//CA对证书的签名值
int valid;//是否是合法证书,1为合法,0为未知
int references;//引用次数,被引用一次则加一
char *name;//证书持有者信息,内容形式为/C=CN/O=ourinfo,该内容在调用d2i_X509的过程中,通过回调函数x509_cb(crypto/asn1/x_x509.c)调用X509_NAME_oneline来设置
CRYPTO_EX_DATA ex_data;//扩展数据结构,用于存放用户自定义的信息
/* These contain copies of various extension values */
long ex_pathlen;//证书路径长度,对应扩展项为NID_basic_constraints
long ex_pcpathlen;
unsigned long ex_flags;//通过“与”计算存放各种标记
unsigned long ex_kusage;//**用法,对应扩展项为NID_key_usage
unsigned long ex_xkusage;//扩展**用法,对应扩展项为NID_ext_key_usage
unsigned long ex_nscert;//Netscape证书类型,对应扩展项为NID_netscape_cert_type
ASN1_OCTET_STRING *skid;//主体**标识,对应扩展项为NID_subject_key_identifier
struct AUTHORITY_KEYID_st *akid;//颁发者**标识,对应扩展项为NID_authority_key_identifier
X509_POLICY_CACHE *policy_cache;//各种策略缓存,对应的策略为NID_policy_constraints、NID_certificate_policies、NID_policy_mappings和NID_inhibit_any_policy
#ifndef OPENSSL_NO_RFC3779
STACK_OF(IPAddressFamily) *rfc3779_addr;
struct ASIdentifiers_st *rfc3779_asid;
#endif
#ifndef OPENSSL_NO_SHA
unsigned char sha1_hash[SHA_DIGEST_LENGTH];//存放证书的sha1摘要值
#endif
X509_CERT_AUX *aux;//辅助信息
} /* X509 */;

 2、X509_CINF struct

typedef struct x509_cinf_st
{
ASN1_INTEGER *version;/* 证书版本,0代表V1,1代表V2 */
ASN1_INTEGER *serialNumber;//证书***
X509_ALGOR *signature;//签名算法
X509_NAME *issuer;//颁发者信息
X509_VAL *validity;//有效期
X509_NAME *subject;//拥有者信息
X509_PUBKEY *key;//拥有者的公钥
ASN1_BIT_STRING *issuerUID;/* [ 1 ] optional in v2 */
ASN1_BIT_STRING *subjectUID;/* [ 2 ] optional in v2 */
STACK_OF(X509_EXTENSION) *extensions;/* [ 3 ] optional in v3 */
} X509_CINF;

   3、EVP_PKEY struct

   

typedef struct evp_pkey_st EVP_PKEY;
typedef struct evp_pkey_st
{
     int type;
     int save_type;
     int references;
     union
     {
     char *ptr;//存放**结构地址
     #ifndef OPENSSL_NO_RSA
     struct rsa_st *rsa; /* RSA */
     #endif
     #ifndef OPENSSL_NO_DSA
     struct dsa_st *dsa; /* DSA */
     #endif
     #ifndef OPENSSL_NO_DH
     struct dh_st *dh; /* DH */
     #endif
     } pkey;
     int save_parameters;
     STACK_OF(X509_ATTRIBUTE) *attributes; /* [ 0 ] */ //存放**属性
    }EVP_PKEY;

  4、RSA struct

   

typedef struct rsa_st RSA;
struct rsa_st
{
    /* The first parameter is used to pickup errors where
     * this is passed instead of aEVP_PKEY, it is set to 0 */
    int pad;
    long version;
    const RSA_METHOD *meth;//RSA_METHOD结构,指明了本RSA**的各种运算函数地址
    /* functional reference if 'meth' is ENGINE-provided */
    ENGINE *engine;//硬件引擎
    BIGNUM *n;   //public modulus
    BIGNUM *e;    //public exponent
    BIGNUM *d;    //private exponent
    BIGNUM *p;    //secret prime factor
    BIGNUM *q;    //secret prime factor
    BIGNUM *dmp1;    //d mod (p-1)
    BIGNUM *dmq1;    //d mod (q-1)
    BIGNUM *iqmp;    //q^(-1) mod p
    /* be careful using this if the RSA structure is shared */
    CRYPTO_EX_DATA ex_data;//扩展数据结构,用于存放用户数据
    int references;//RSA结构引用数
    int flags;
    /* Used to cache montgomery values */
    BN_MONT_CTX *_method_mod_n;
    BN_MONT_CTX *_method_mod_p;
    BN_MONT_CTX *_method_mod_q;
    /* all BIGNUM values are actually in the following data, if it is not
     * NULL */
    char *bignum_data;
    BN_BLINDING *blinding;
    BN_BLINDING *mt_blinding;
};

   5、BIGNUM struct

   

typedef struct bignum_st BIGNUM;
struct bignum_st
{
       BN_ULONG *d;//BN_ULONG(应系统而异,win32下为4个字节)数组指针首地址,大数就存放在这里面,不过是倒放的
       int top;//用来指明大数占多少个BN_ULONG空间
       int dmax;//d数组的大小
       int neg;//是否为负数,如果为1,则是负数,为0,则为正数
       int flags;//用于存放一些标记,比如flags含有BN_FLG_STATIC_DATA时,表明d的内存是静态分配的;含有BN_FLG_MALLOCED时,d的内存是动态分配的
};

  6、STACK_OF struct

   

#define STACK_OF(type) STACK
typedef struct stack_st
{
      int num;//堆栈中存放数据的个数
      char **data;//用于存放数据地址,每个数据地址存放在data[0]到data[num-1]中
      int sorted;//堆栈是否已排序,如果排序则值为1,否则为0,堆栈数据一般是无序的,只有当用户调用了sk_sort操作,其值才为1
      int num_alloc;//
      int (*comp)(const char * const *, const char * const *);//堆栈内存放数据的比较函数地址,此函数用于排序和查找操作
} STACK;

  7、PKCS12 struct

typedef struct
{
ASN1_INTEGER *version;
PKCS12_MAC_DATA *mac;
PKCS7 *authsafes;
} PKCS12;

version为版本,mac用于存放MAC信息以及对称**相关的信息,authsafespkcs7结构,用于存放的证书、crl以及私钥等各种信息  

   下面是测试代码,先从PEM中提取信息保存到P12文件中,然后从p12中提取**用来加密和解密数据:

#include <stdio.h>
#include <stdlib.h>
#include <openssl/md5.h>   
#include <stdio.h> 
#include <openssl/rsa.h> 
#include <openssl/evp.h> 
#include <openssl/objects.h> 
#include <openssl/x509.h> 
#include <openssl/err.h> 
#include <openssl/pem.h> 
#include <openssl/pkcs12.h>  
#include <openssl/ssl.h> 
extern "C"
{
#include <openssl/applink.c>
}
#pragma comment(lib, "libeay32.lib")    
#pragma comment(lib, "ssleay32.lib")    
#define CERTS_FILE "mmm.pem"
#define CLNT_KEY CERTS_FILE
//使用*.p12作为扩展名会使得Windows操作系统正确认识和处理它
#define PKCS12_FILE "mao1.p12"
void print(const char *promptStr,unsigned char *data,int len )
{
    int i;
    printf("n==%s[输出长度=%d]=====n",promptStr,len);
    for(i = 0; i < len; i++)
    printf("%02x", data[i]);
    printf("n=======================n");
}
int main(int argc, char *argv[])
{
    X509 *cert_clnt,*cert_tmp;
    EVP_PKEY *pkey;
    EVP_PKEY *pkey_frompkcs12;
    STACK_OF(X509) *ca_chain=NULL;
    PKCS12 *pkcs12;
    FILE *fp;
    char* pass="123456";
    char* name="Client Private/Publication Key";
    char plainText[]="[For a test to read/write pkcs12 object]";
    unsigned char encData[512];
    char decData[512];
    int len=0;
    OpenSSL_add_all_algorithms();
    ERR_load_crypto_strings();
    //seed_prng();
    //读入私钥对象
    if ( NULL == (fp = fopen(CLNT_KEY, "r")) ||
        NULL == (pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL)))
    {
        printf("读客户端私钥出错");
        return -1;
    }
    rewind(fp);
    //读入证书链
    ca_chain = sk_X509_new_null();
    while(1)
    {
        //文件指针被移动,所以循环可以读取所有证书
        if ( NULL==(cert_tmp = PEM_read_X509(fp, NULL, NULL, NULL)))
        {
            break;
        }
        sk_X509_push(ca_chain, cert_tmp);
        if( 1==ca_chain->num )
        {
            cert_clnt=cert_tmp;//客户端证书
        }
    }
    fclose(fp);
    printf("%d个证书在证书文件%s中n",ca_chain->num,CERTS_FILE);
    if(ca_chain->num==0)
    {
        printf("没有证书在%sn",CERTS_FILE);
        return -1;
    }
      //ca_chain=NULL;
    //创建PKCS12对象
    pkcs12=PKCS12_create(
        pass, //对象保护口令
        name, //对象名称
        pkey, //要保护的私钥
        cert_clnt, //对应私钥的证书对象
        ca_chain, //用于验证证书的证书链
        0,0,0,0,0 //其它缺省或者未指定参数
        );
    if( NULL==pkcs12)
    {printf("创建PKCS12对象时出错");
    return -1;
    }
                                    
                                   
    //将对象写入文件
    if ( NULL == (fp = fopen(PKCS12_FILE, "w")) )
    {
        printf("以写方式打开文件%s时出错n",PKCS12_FILE);
        return -1;
    }
    if ( i2d_PKCS12_fp(fp,pkcs12) != 1)
    {printf("将pkcs12对象写入文件时出错n");
    return -1;
    }
                                        
                                   
    fclose(fp);
                                   
    if ( NULL == (fp = fopen(PKCS12_FILE, "r")) )
    {
        printf("以读方式打开文件%s时出错n",PKCS12_FILE);
        return -1;
    }
    if( NULL==(pkcs12=d2i_PKCS12_fp(fp, NULL)) )
    {printf("从文件中读pkcs12对象时出错"); 
    return -1;
    }
    sk_X509_pop_free(ca_chain,X509_free);
//  EVP_PKEY_free(pkey);//释放该公钥对象
    ca_chain=NULL;
    cert_clnt=NULL;
    //从PKCS12对象中解析出私钥,证书链,以及证书。
    //当然由于pkcs12对象受密码保护,所以要输入保护密码
    if( PKCS12_parse(pkcs12,pass, &pkey_frompkcs12,&cert_clnt,&ca_chain)!=1)
    {
        printf("解析pkcs12对象时出错"); 
        return -1;
    }
//  PKCS7 *authsafebag = pkcs12->authsafes;
//  STACK_OF(PKCS12_SAFEBAG)* pkcs12_safebag = PKCS12_unpack_p7data(authsafebag);
                                    
//  char* temp = sk_value(pkcs12_safebag,1);
//
//  PKCS12_SAFEBAG* pkcs12_temp = (PKCS12_SAFEBAG*)temp;
//  ASN1_OBJECT* type = pkcs12_temp->type;
//
//  int nsize = BN_num_bytes(pkey_frompkcs12->pkey.rsa->n);
//  unsigned char* ndata = new unsigned char[nsize];
//
//  int ret = BN_bn2bin(pkey_frompkcs12->pkey.rsa->n,ndata);
//
//  int esize = BN_num_bytes(pkey_frompkcs12->pkey.rsa->e);
//  unsigned char* edata = new unsigned char[esize];
//
//  ret = BN_bn2bin(pkey_frompkcs12->pkey.rsa->e,edata);
//  int dsize = BN_num_bytes(pkey_frompkcs12->pkey.rsa->d);
//  unsigned char* ddata = new unsigned char[dsize];
//
//   ret = BN_bn2bin(pkey_frompkcs12->pkey.rsa->d,ddata);
//  int psize = BN_num_bytes(pkey_frompkcs12->pkey.rsa->p);
//  unsigned char* pdata = new unsigned char[psize];
//
//  ret = BN_bn2bin(pkey_frompkcs12->pkey.rsa->p,pdata);
//  int qsize = BN_num_bytes(pkey_frompkcs12->pkey.rsa->q);
//  unsigned char* qdata = new unsigned char[qsize];
//
//  ret = BN_bn2bin(pkey_frompkcs12->pkey.rsa->q,qdata);
//  int dmp1size = BN_num_bytes(pkey_frompkcs12->pkey.rsa->dmp1);
//  unsigned char* dmp1data = new unsigned char[dmp1size];
//
//  ret = BN_bn2bin(pkey_frompkcs12->pkey.rsa->dmp1,dmp1data);
//  int dmqsize = BN_num_bytes(pkey_frompkcs12->pkey.rsa->dmq1);
//  unsigned char* dmq1data = new unsigned char[dmqsize];
//
//  ret = BN_bn2bin(pkey_frompkcs12->pkey.rsa->dmq1,dmq1data);
//  int idmpsize = BN_num_bytes(pkey_frompkcs12->pkey.rsa->iqmp);
//  unsigned char* iqmpdata = new unsigned char[idmpsize];
//
//  ret = BN_bn2bin(pkey_frompkcs12->pkey.rsa->iqmp,iqmpdata);
//
//  delete[] ndata;
//  delete[] edata;
//  delete[] ddata;
//  delete[] pdata;
//  delete[] qdata;
//  delete[] dmp1data;
//  delete[] dmq1data;
//  delete[] iqmpdata;
    printf("读取并解析pkcs12对象成功n pkcs 文件: n"
        " %sn 证书编号:%dn 证书名:%sn",
        PKCS12_FILE,ca_chain->num,cert_clnt->name);
    //利用解析出来的私钥去加密和解密
    len=EVP_PKEY_encrypt(encData,(const unsigned char*)plainText,
        sizeof(plainText),pkey_frompkcs12);
    if(len==-1)
    {printf("EVP_PKEY_encrypt失败"); 
    return -1;
    }
                                        
    print("加密后的数据是:",encData,len);
    //用公钥解密
    len=EVP_PKEY_decrypt((unsigned char*)
        decData,encData,len,pkey_frompkcs12);
    if(len==-1)
    {printf("EVP_PKEY_decrypt 失败"); 
    return -1;
    }
                                        
    //print("解密后的数据是:",(unsigned char *)decData,len);
    printf("n 明文是[长度=%d]:%sn",len,decData);
    printf("n click any key to continue.");getchar();
    return 0;
}