使用si查看openssl源码,找不X509_new
的定义。想要知道该函数的定义,还得从ASN1相关部分说起。
ASN1是规定了一种数字对象的描述方法和标准,定义了诸多的基础类型(INTEGER和BIT STRING),并可以通过这些基础类型构建用户自定义类型,后面讨论的证书类型就是由这些基础类型构建的。DER是一种编码方法,描述了如何将这些类型的对象转换成为二进制,这样这些定义的数据就可以进行传输和存储。
由于后面的分析中有的宏定义存在不同的分支,这里面暂且假设在编译宏没有定义OPENSSL_EXPORT_VAR_AS_FUNCTION
,后面均以此为准,分析如下:
首先要提到asn1模块中重要的两个结构体,这两个结构体相当于所有自定义对象的类,如下所示:
struct ASN1_ITEM_st {
char itype; /* The item type, primitive, SEQUENCE, CHOICE
* or extern */
long utype; /* underlying type */
const ASN1_TEMPLATE *templates; /* If SEQUENCE or CHOICE this contains
* the contents */
long tcount; /* Number of templates if SEQUENCE or CHOICE */
const void *funcs; /* functions that handle this type */
long size; /* Structure size (usually) */
const char *sname; /* Structure name */
};
struct ASN1_TEMPLATE_st {
unsigned long flags; /* Various flags */
long tag; /* tag, not used if no tagging */
unsigned long offset; /* Offset of this field in structure */
const char *field_name; /* Field name */
ASN1_ITEM_EXP *item; /* Relevant ASN1_ITEM or ASN1_ADB */
};
ASN1_ITEM_st
相当于定义了一个抽象的类,对于每一个具体的用户数据,比如证书相关结构,都是属于这种类型类的具体对象,例如X509_it,该对象后面会详细介绍。同时在 ASN1_ITEM_st
结构中包含了 ASN1_TEMPLATE_st
结构指针,可以理解为一个数组。而ASN1_TEMPLATE_st
结构包含了ASN1_ITEM_st
结构(通过宏定义知道typedef const ASN1_ITEM ASN1_ITEM_EXP; ASN1_ITEM_EXP
就是ASN1_ITEM
类型,也就是说 ASN1_TEMPLATE_st
中的item是一个ASN1_ITEM_st
类型)。其本质相当于 ASN1_ITEM_st
结构中包含了 ASN1_ITEM_st
这种类型的数组。这是一种递归结构,保证了一个大的对象里面可以包含同样类的较小的对象。
以数字证书为例,x509_it
是一个大的 ASN1_ITEM_st
结构类型,我们知道证书由证书体,算法以及签名部分组成,因此在其中还包含了X509_CINF_it,X509_ALGOR_it,ASN1_BIT_STRING_it
(因为签名是加密的,相当于BIT STRING类型)变量,而这些次一级的对象都存在于ASN1_ITEM x509_it
的 const ASN1_TEMPLATE *templates;
中的。下面具体的详细说明一下:
在crypto/X509/x_x509.c文件中存在如下的宏定义(这种结构一共有几种,搜索一下)
ASN1_SEQUENCE_ref(X509, x509_cb) = {
ASN1_EMBED(X509, cert_info, X509_CINF),
ASN1_EMBED(X509, sig_alg, X509_ALGOR),
ASN1_EMBED(X509, signature, ASN1_BIT_STRING)
} ASN1_SEQUENCE_END_ref(X509, X509)
展开后如下:
static const ASN1_AUX X509_aux = {NULL, ASN1_AFLG_REFCOUNT, offsetof(X509, references), offsetof(X509, lock), x509_cb, 0};
static const ASN1_TEMPLATE X509_seq_tt[] ={
{ASN1_TFLG_EMBED,0,offsetof(X509,cert_info),”cert_info”, &X509_CINF_it},
{ASN1_TFLG_EMBED,0,offsetof(X509,sig_alg),”sig_alg”, &X509_ALGOR_it},
{ASN1_TFLG_EMBED,0,offsetof(X509,signature),”signature”, &ASN1_BIT_STRING_it},
};
static const ASN1_ITEM X509 _it = {
ASN1_ITYPE_SEQUENCE,
V_ASN1_SEQUENCE,
X509_seq_tt,
sizeof(X509_seq_tt) / sizeof(ASN1_TEMPLATE),
X509_aux,
sizeof(X509),
X509
};
可以看到上述的宏定义其实定义了三个变量。 X509 _it
一个 ASN1_ITEM
类型的对象;X509_seq_tt[]
表示一个 ASN1_TEMPLATE
类型的数组,根据上面的分析,该数组的作用相当于一个容器,用来承载更低一级的ASN1_ITEM
对象,例如X509_CINF_it
以及 X509_ALGOR_it
等。
当然存在这样一个疑问X509_CINF_it
这个对象在哪里?在crypto/X509/x_x509.c文件的开始,有如下的宏:
ASN1_SEQUENCE_enc(X509_CINF, enc, 0) = {
ASN1_EXP_OPT(X509_CINF, version, ASN1_INTEGER, 0),
ASN1_EMBED(X509_CINF, serialNumber, ASN1_INTEGER),
ASN1_EMBED(X509_CINF, signature, X509_ALGOR),
ASN1_SIMPLE(X509_CINF, issuer, X509_NAME),
ASN1_EMBED(X509_CINF, validity, X509_VAL),
ASN1_SIMPLE(X509_CINF, subject, X509_NAME),
ASN1_SIMPLE(X509_CINF, key, X509_PUBKEY),
ASN1_IMP_OPT(X509_CINF, issuerUID, ASN1_BIT_STRING, 1),
ASN1_IMP_OPT(X509_CINF, subjectUID, ASN1_BIT_STRING, 2),
ASN1_EXP_SEQUENCE_OF_OPT(X509_CINF, extensions, X509_EXTENSION, 3)
} ASN1_SEQUENCE_END_enc(X509_CINF, X509_CINF)
该宏即定义了 X509_CINF_it
,同理像 X509_ALGOR_it
在crypto/asn1/x_algor.c中有对应宏声明。
ASN1_SEQUENCE(X509_ALGOR) = {
ASN1_SIMPLE(X509_ALGOR, algorithm, ASN1_OBJECT),
ASN1_OPT(X509_ALGOR, parameter, ASN1_ANY)
} ASN1_SEQUENCE_END(X509_ALGOR)
而最终会第归到ASN1模块定义的最基础的ASN1_BIT_STRING_it
。同样的在crypto/asn1/tasn_typ.c中定义了有如下宏:
IMPLEMENT_ASN1_STRING_FUNCTIONS(ASN1_BIT_STRING)
,该宏不仅实现了ASN1_BIT_STRING_it
对象的定义,同时还实现了操作该对象的四个函数包括d2i_ASN1_BIT_STRING,i2d_ASN1_BIT_STRING,X509_ASN1_BIT_STRING,X509_ASN1_BIT_STRING
。当然像ASN1_INTEGER
在同样的文件中也是有定义的。
前面只是说明了X509_it
对象的定义,这些对象的意义是ASN1规范所描述的数据类型。asn1模块的意义在于将der编码的数据流转换为C 语言内部定义的结构,或者相反的过程,而上述定义的变量如X509_it
正是用来辅助这种转换的过程。例如将经过der编码的数据转化为内部的结构体X509_st,因此需要一些转换的函数。
crypto/X509/x_x509.c中分别定义了IMPLEMENT_ASN1_FUNCTIONS(X509)
以及IMPLEMENT_ASN1_FUNCTIONS(X509_CINF)
宏表示对应的函数,这个宏展开还是比较简单,其实就是d2i_X509,i2d_X509,X509_new,X509_free
四个函数的定义。
因此可以看到对于复杂的结构如证书,变量的定义和对变量的操作是分开来的。而对于基础的结构ASN1_BIT_STRING
则是使用一个宏同时实现了变量的定义和相关函数的操作。
从X509_new的命名可以看出,这里面从命名方面已经开始向面向对象的C++靠拢了。事实上我也是一直说X509_it
是一个对象,C语言确实一种面向过程的语言,但是这里面却有面向对象的思想在其中。
本文为优快云村中少年原创文章,转载记得加上小尾巴偶,博主链接这里。