第二十四章 通用数据结构 / 24.7 X509_EXTENSION

       本结构用于存放各种扩展项信息。

       1)结构定义 

数字证书扩展项,定义在crypto/x509/x509.h中,如下:

typedef struct X509_extension_st

       {

              ASN1_OBJECT *object;

              ASN1_BOOLEAN critical;

              ASN1_OCTET_STRING *value;

       } X509_EXTENSION;

       其中object指明是哪种扩展项;critical指明是否为关键扩展项,为0xFF时为关键扩展项,-1为非关键扩展项;valueDER编码的具体扩展项的值。该结构的DER编解码在crypto/asn1/x_exten.c中由宏实现,包括newfreei2dd2idup函数。扩展项的DER编解码可直接采用i2dd2i来完成,也可用采用openssl提供的其他函数。

       2)通过X509V3_EXT_METHOD进行DER编解码

Openssl通过X509V3_EXT_METHOD来实现对扩展项的编解码。X509V3_EXT_METHOD定义在crypto/x509v3/x509v3.h中,如下:

       struct v3_ext_method

{

int ext_nid;

int ext_flags;

ASN1_ITEM_EXP *it;

X509V3_EXT_NEW ext_new;

X509V3_EXT_FREE ext_free;

X509V3_EXT_D2I d2i;

X509V3_EXT_I2D i2d;

X509V3_EXT_I2S i2s;

X509V3_EXT_S2I s2i;

X509V3_EXT_I2V i2v;

X509V3_EXT_V2I v2i;

              X509V3_EXT_I2R i2r;

X509V3_EXT_R2I r2i;

              void *usr_data;

};

typedef struct v3_ext_method X509V3_EXT_METHOD;

该结构以ext_nid表示是何种扩展项,以itd2ii2d函数来指明来它的DER编解码函数。这样,只要知道了ext_nid,就能够对数据进行DER编解码。Openssl对于每个支持的扩展项都实现了上述数据结构,这些文件都在crypto/x509v3目录下:

Ø         v3_akey.c:权威密钥标识,实现了AUTHORITY_KEYIDDER编解码和X509V3_EXT_METHOD

Ø         v3_alt.c:颁发者别名,实现了GENERAL_NAMES509V3_EXT_METHOD

Ø         v3_bcons.c:基本约束,实现了BASIC_CONSTRAINTSDER编解码和509V3_EXT_METHOD

Ø         v3_cpols.c:证书策略,实现了CERTIFICATEPOLICIESDER编解码和509V3_EXT_METHOD

Ø         v3_crld.cCRL发布点,实现了CRL_DIST_POINTSDER编解码和509V3_EXT_METHOD

Ø         v3_enum.c:证书撤销原因,实现了其509V3_EXT_METHOD

Ø         v3_extku.c:扩展密钥用法,实现了EXTENDED_KEY_USAGEDER编解码,扩展密钥和ocsp_accresp509V3_EXT_METHOD

Ø         v3_info.c:权威信息获取,实现了AUTHORITY_INFO_ACCESSDER编解码,v3_infov3_sinfo两个509V3_EXT_METHOD

Ø         v3_int.c:实现了v3_crl_numv3_delta_crlv3_inhibit_anyp(继承任何策略)509V3_EXT_METHOD

Ø         v3_ncons.c:名字约束,实现了NAME_CONSTRAINTSDER编解码和它的509V3_EXT_METHOD

Ø         v3_ocsp.c:实现了OCSP相关的多个扩展项的509V3_EXT_METHOD

Ø         v3_pci.c:实现了代理证书扩展项的509V3_EXT_METHOD

Ø         v3_pcons.c:策略约束,实现了POLICY_CONSTRAINTSDER编解码和509V3_EXT_METHOD

Ø         v3_pku.c:密钥有效期,实现了PKEY_USAGE_PERIODDER编解码和它的509V3_EXT_METHOD

Ø         v3_pmaps.c:策略映射,实现了POLICY_MAPPINGSDER编解码和它的509V3_EXT_METHOD

Ø         v3_skey.c:主体密钥标识,实现了该扩展项的509V3_EXT_METHOD

Ø         v3_sxnet.c:实现了SXNETDER编解码和它的509V3_EXT_METHOD

openssl509V3_EXT_METHOD维护了两个表供调用者查找和使用。一个表定义在crypto/x509v3/ext_dat.h中,如下:

static X509V3_EXT_METHOD *standard_exts[] = {

&v3_nscert,

&v3_ns_ia5_list[0],

&v3_ns_ia5_list[1],

/* 其他 */

&v3_policy_mappings,

&v3_inhibit_anyp

};

       该表是一个全局表。另外一个表在crypto/x509v3/v3_lib.c中,是一个全局的X509V3_EXT_METHOD堆栈,定义如下:

       static STACK_OF(X509V3_EXT_METHOD) *ext_list = NULL;

       当用户其他扩展的时候,可以实现自己的X509V3_EXT_METHOD,并调用X509V3_EXT_add函数放入堆栈。

       当用户根据扩展项的nid查找对应的X509V3_EXT_METHOD时,首先查找standard_exts,然后在查找ext_list。找到后,用户就能根据X509V3_EXT_METHOD中的各种方法来处理扩展项(比如,DER编解码)

       将具体的扩展项数据结构(不是指X509_EXTENSION而是一个具体扩展项,比如NAME_CONSTRAINTS)合成X509_EXTENSION时,可以采用如下函数:

       X509_EXTENSION *X509V3_EXT_i2d(int ext_nid, int crit, void *ext_struc)

       其中ext_nid指明了是那种扩展项,crit表明是否为关键扩展项,ext_struc为具体扩展项数据结构地址(比如NAME_CONSTRAINTS的地址),返回值为一个已经构造好的X509_EXTENSION。该函数首先根据ext_nid查表来获取具体扩展项的的X509V3_EXT_METHOD,然后根据X509V3_EXT_METHOD中的it或者i2d函数将具体扩展项(比如NAME_CONSTRAINTS)进行DER编码,最后再调用X509_EXTENSION_create_by_NID来生成一个扩展项并返回。

       X509_EXTENSION中提取出具体扩展项的数据结构可以采用如下函数:

void *X509V3_EXT_d2i(X509_EXTENSION *ext)

该函数首先根据X509_EXTENSION来获取是那种扩展项,并查找X509V3_EXT_METHOD表,然后根据对应的d2i函数解码X509_EXTENSION-> value中的DER编码数据,生成具体的扩展项数据结构并返回。

上述两个函数是具体扩展项和X509_EXTENSION相互转化最基本的函数,很多函数都基于它们。

主要函数:

Ø         X509V3_EXT_add:在扩展X509V3_EXT_METHODext_list中添加一个方法。

Ø         X509V3_EXT_get_nid:根据nid来查找X509V3_EXT_METHOD

Ø         X509V3_EXT_get:根据扩展项来查找X509V3_EXT_METHOD,它调用了X509V3_EXT_get_nid

Ø         X509V3_EXT_add_alias:添加一个X509V3_EXT_METHOD,使具有相同方法的X509V3_EXT_METHOD有不同的扩展项nid

Ø         X509V3_get_d2i:从扩展项堆栈中查找具体的扩展项,并返回具体扩展项数据结构地址。

Ø         X509V3_EXT_print:打印单个扩展项。

Ø         int X509V3_add1_i2d(STACK_OF(X509_EXTENSION) **x, int nid, void *value,int crit, unsigned long flags)

往扩展项堆栈中添加一个具体的扩展项value,该具体的扩展项是其数据结构地址,添加扩展项时,根据输入参数flags可以处理扩展项冲突。flags可以的值定义在x509v3.h中,如下:

#define X509V3_ADD_DEFAULT        0L

#define X509V3_ADD_APPEND         1L

#define X509V3_ADD_REPLACE        2L

#define X509V3_ADD_REPLACE_EXISTING    3L

#define X509V3_ADD_KEEP_EXISTING   4L

#define X509V3_ADD_DELETE          5L

#define X509V3_ADD_SILENT          0x10

由于flags值的不同,本函数的操作可以有如下情况:

a)扩展项堆栈中没有nid对应的扩展项,此时如果flagsX509V3_ADD_REPLACE_EXISTINGX509V3_ADD_DELETE 则报错:无此扩展项;

b)    扩展项堆栈中有nid对应的扩展项,如果flagsX509V3_ADD_KEEP_EXISTING,成功返回;如果flagsX509V3_ADD_DEFAULT 报错,表明此扩展项已经存在;如果flagsX509V3_ADD_DELETE,则删除这个扩展项;如果flags  X509V3_ADD_REPLACE_EXISTING,则替换此扩展项。

编程示例1

       调用函数X509_EXTENSION_create_by_NIDX509_EXTENSION_create_by_OBJ生成扩展项,并调用X509_EXTENSION_get_objectX509_EXTENSION_get_dataX509_EXTENSION_get_critical获取扩展项信息;    调用X509_EXTENSION_set_objectX509_EXTENSION_set_criticalX509_EXTENSION_set_data设置扩展项信息。这种构造扩展项的方法是比较烦琐的方法。

              #include  <openssl/x509.h>

              #include  <openssl/x509v3.h>

              #include  <openssl/objects.h>

              int    main()

              {

                     X509_EXTENSION             *ext=NULL;          /* 必须=NULL */

                     ASN1_OCTET_STRING      *data,*data2;

                     time_t                          t;

                     PKEY_USAGE_PERIOD       *period,*period2;

                     int                                len,ret,buflen=100;

                     unsigned char        *p,*der,*der2;

                     ASN1_OBJECT                   *obj=NULL;

                     char                      buf[100];

                     BIO                             *b;

             

                     /* 构造内部数据结构 */

                     period=PKEY_USAGE_PERIOD_new();

                     t=1;

                     /* 从时间197011000秒往后算时间,t=1表示1*/

                     period->notBefore=ASN1_GENERALIZEDTIME_set(period->notBefore,t);

                     t=100;

                     period->notAfter=ASN1_GENERALIZEDTIME_set(period->notAfter,t);  

                     /* der编码 */

                     len=i2d_PKEY_USAGE_PERIOD(period,NULL);

                     der=(unsigned char *)malloc(len);

                     p=der;

                     len=i2d_PKEY_USAGE_PERIOD(period,&p);

             

                     data=ASN1_OCTET_STRING_new();

                     ASN1_OCTET_STRING_set(data,der,len);

              #if 1

                            X509_EXTENSION_create_by_NID(&ext,NID_private_key_usage_period,1,data);

              #else

                            obj=OBJ_nid2obj(NID_private_key_usage_period);

                            X509_EXTENSION_create_by_OBJ(&ext,obj,1,data);

              #endif

                     /* get 函数*/

                     obj=X509_EXTENSION_get_object(ext);

                     OBJ_obj2txt(buf,buflen,obj,0);

                     printf("extions obj : %s\n",buf);

                     data=X509_EXTENSION_get_data(ext);

                     b=BIO_new(BIO_s_file());

                     BIO_set_fp(b,stdout,BIO_NOCLOSE);

                     ASN1_STRING_print(b,data);

                     ret=X509_EXTENSION_get_critical(ext);

                     if(ret==1)

                     {

                            printf("关键扩展项\n");

                     }

                     else

                     {

                            printf("非关键扩展项\n");

                     }

                     /* set 函数 */

                     ret=X509_EXTENSION_set_object(ext,obj);

                     if(ret!=1)

                     {

                            printf("X509_EXTENSION_set_object err\n");

                     }

                     ret=X509_EXTENSION_set_critical(ext,0);/* 设置为非关键扩展 */

                     if(ret!=1)

                     {

                            printf("X509_EXTENSION_set_critical err\n");

                     }    

                     period2=PKEY_USAGE_PERIOD_new();

                     t=(2006-1970)*365*24*3600;

                     period2->notBefore=ASN1_GENERALIZEDTIME_set(period2->notBefore,t);

                     t=t+10*365*24*3600;

                     period2->notAfter=ASN1_GENERALIZEDTIME_set(period2->notAfter,t);

                     /* der编码 */

                     len=i2d_PKEY_USAGE_PERIOD(period2,NULL);

                     der2=(unsigned char *)malloc(len);

                     p=der2;

                     len=i2d_PKEY_USAGE_PERIOD(period2,&p);

                     data2=ASN1_OCTET_STRING_new();

                     ASN1_OCTET_STRING_set(data2,der2,len);

                     ret=X509_EXTENSION_set_data(ext,data2);             /* 设置新的时间段 */

                     if(ret!=1)

                     {

                            printf("X509_EXTENSION_set_data err\n");

                     }

                     PKEY_USAGE_PERIOD_free(period);

                     PKEY_USAGE_PERIOD_free(period2);

                     free(der);

                     free(der2);

                     ASN1_OCTET_STRING_free(data);

                     ASN1_OCTET_STRING_free(data2);

                     X509_EXTENSION_free(ext);

                     return 0;

              }

       编程示例2

       通过X509V3_EXT_METHOD来构造扩展项,简单。

#include <openssl/x509v3.h>

int       main()

{

    X509_EXTENSION             *ext=NULL;

    STACK_OF(X509_EXTENSION)*exts=NULL;

    time_t                          t;

    PKEY_USAGE_PERIOD       *period;

    int                                ret;

 

    /* 构造内部数据结构 */

    period=PKEY_USAGE_PERIOD_new();

    t=1;

    /* 从时间197011000秒往后算时间,t=1表示1*/

    period->notBefore=ASN1_GENERALIZEDTIME_set(period->notBefore,t);

    t=100;

    period->notAfter=ASN1_GENERALIZEDTIME_set(period->notAfter,t);  

    /* 根据具体的扩展项构造一个X509_EXTENSION */

    ext=X509V3_EXT_i2d(NID_private_key_usage_period,1, period);

    /* 根据具体的扩展项构造一个X509_EXTENSION堆栈*/

    ret=X509V3_add1_i2d(&exts,NID_private_key_usage_period, period,1,X509V3_ADD_DEFAULT);

    X509_EXTENSION_free(ext);

    sk_X509_EXTENSION_pop_free(exts,X509_EXTENSION_free);

    return 0;

}