本结构用于存放各种扩展项信息。
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为非关键扩展项;value为DER编码的具体扩展项的值。该结构的DER编解码在crypto/asn1/x_exten.c中由宏实现,包括new、free、i2d、d2i和dup函数。扩展项的DER编解码可直接采用i2d和d2i来完成,也可用采用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表示是何种扩展项,以it、d2i和i2d函数来指明来它的DER编解码函数。这样,只要知道了ext_nid,就能够对数据进行DER编解码。Openssl对于每个支持的扩展项都实现了上述数据结构,这些文件都在crypto/x509v3目录下:
Ø v3_akey.c:权威密钥标识,实现了AUTHORITY_KEYID的DER编解码和X509V3_EXT_METHOD;
Ø v3_alt.c:颁发者别名,实现了GENERAL_NAMES的509V3_EXT_METHOD;
Ø v3_bcons.c:基本约束,实现了BASIC_CONSTRAINTS的DER编解码和509V3_EXT_METHOD;
Ø v3_cpols.c:证书策略,实现了CERTIFICATEPOLICIES的DER编解码和509V3_EXT_METHOD;
Ø v3_crld.c:CRL发布点,实现了CRL_DIST_POINTS的DER编解码和509V3_EXT_METHOD;
Ø v3_enum.c:证书撤销原因,实现了其509V3_EXT_METHOD;
Ø v3_extku.c:扩展密钥用法,实现了EXTENDED_KEY_USAGE的DER编解码,扩展密钥和ocsp_accresp的509V3_EXT_METHOD;
Ø v3_info.c:权威信息获取,实现了AUTHORITY_INFO_ACCESS的DER编解码,v3_info和v3_sinfo两个509V3_EXT_METHOD;
Ø v3_int.c:实现了v3_crl_num、v3_delta_crl和v3_inhibit_anyp(继承任何策略)的509V3_EXT_METHOD;
Ø v3_ncons.c:名字约束,实现了NAME_CONSTRAINTS的DER编解码和它的509V3_EXT_METHOD;
Ø v3_ocsp.c:实现了OCSP相关的多个扩展项的509V3_EXT_METHOD;
Ø v3_pci.c:实现了代理证书扩展项的509V3_EXT_METHOD;
Ø v3_pcons.c:策略约束,实现了POLICY_CONSTRAINTS的DER编解码和509V3_EXT_METHOD;
Ø v3_pku.c:密钥有效期,实现了PKEY_USAGE_PERIOD的DER编解码和它的509V3_EXT_METHOD;
Ø v3_pmaps.c:策略映射,实现了POLICY_MAPPINGS的DER编解码和它的509V3_EXT_METHOD;
Ø v3_skey.c:主体密钥标识,实现了该扩展项的509V3_EXT_METHOD;
Ø v3_sxnet.c:实现了SXNET的DER编解码和它的509V3_EXT_METHOD。
openssl为509V3_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_METHOD表ext_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
#define
X509V3_ADD_APPEND
#define X509V3_ADD_REPLACE
#define X509V3_ADD_REPLACE_EXISTING
#define X509V3_ADD_KEEP_EXISTING
#define X509V3_ADD_DELETE
#define X509V3_ADD_SILENT 0x10
由于flags值的不同,本函数的操作可以有如下情况:
a)扩展项堆栈中没有nid对应的扩展项,此时如果flags为X509V3_ADD_REPLACE_EXISTING或X509V3_ADD_DELETE 则报错:无此扩展项;
b) 扩展项堆栈中有nid对应的扩展项,如果flags为X509V3_ADD_KEEP_EXISTING,成功返回;如果flags是X509V3_ADD_DEFAULT 报错,表明此扩展项已经存在;如果flags是X509V3_ADD_DELETE,则删除这个扩展项;如果flags是 X509V3_ADD_REPLACE_EXISTING,则替换此扩展项。
编程示例1:
调用函数X509_EXTENSION_create_by_NID和X509_EXTENSION_create_by_OBJ生成扩展项,并调用X509_EXTENSION_get_object、X509_EXTENSION_get_data和X509_EXTENSION_get_critical获取扩展项信息; 调用X509_EXTENSION_set_object、X509_EXTENSION_set_critical和X509_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;
/*
从时间
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;
/* 从时间
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;
}