OpenSSL中文手册之ASN1库详解(未完待续)

openssl之ASN.1系列之1—引言和ASN.1概述

【引言】
ASN.1全称为Abstract Syntax NotationOne,是一种描述数字对象的方法和标准。openssl的编码方法就是基于该标准的,目前,很多其他软件的编码方法也是基于该标准。对于直接使用openssl的API或者应用程序来说,可能对ASN.1的了解并不需要很清楚,但是为了使大家对后续介绍的各个API有一个更深刻的编码知识基础,所以对该ASN.1以及openssl相应提供的API处理函数作介绍。

【ASN.1概述】
ASN.1作为一个数字对象描述标准,包括了两部分,分别为数据描述语言(ISO8824)和数据编码规则(ISO8825)。ASN.1的数据描述语言标准允许用户自定义基本数据类型,并可以通过简单的数据类型组成更复杂的数据类型。比如一个复杂的数据对象,如X509证书,就是在其它一些数据类型上定义的,而其它数据类型又是在更基本的数据类型上建立的,直到回溯到定义的最基本的数据类型。
比如ASN.1定义的X509证书的一个子域Validity(证书有效期)就定义如下:
Validity ::= SEQUENCE
{
notBefore UTCTIME,
notAfter UTCTIME
}
其意义就是定义Validity为一个有序序列,由两个个UTCTIME类型的数据notBefore和notAfter组成。然后,就需要找出UTCTIME是怎么定义的,当然,事实上UTCTIME是ASN.1定义的一个基本的时间数据类型。
在上述数据定义的基础上,ASN.1定义了一组编码规则,以规定怎么将上述描述的对象转换成应用程序能够处理和进行传输的二进制编码形式。ASN.1定义了多种编码方法,包括了BER, DER, PER, 和XER等,不过,虽然最基本最常用的编码方式是BER(Basic EncodingRules),但是由于该编码方法可能对一个相同的对象有几种不同的合法二进制编码,所以在openssl里面使用的是BER的子集DER(Distinguished Encoding Rules),使用DER编码方法,对于每一个ASN.1对象,其相应的二进制编码是唯一的。

ASN.1里定义的每个基本对象都有一个对应的数字标识tag,在进行二进制编码的时候需要使用该标志。
【ASN.1定义的基本数据类型】
下面列出ASN.1定义的部分基本数据类型,其各字段的意义如下:
[数据类型]-[数据说明]-[Tag(16进制)]
[BOOLEAN]-[有两个值:false或true]-[01]
[INTEGER]-[整型值]-[02]
[BIT STRING]-[0位或多位]-[03]
[OCTET STRING]-[0字节或多字节]-[04]
[NULL]-[NULL值]-[05]
[OBJECT IDENTIFIER]-[相应于一个对象的独特标识数字]-[06]
[OBJECT DESCRIPTOR]-[一个对象的简称]-[07]
[EXTERNAL]-[ASN.1没有定义的数据类型]-[08]
[REAL]-[实数值]-[09]
[ENUMERATED]-[数值列表,这些数据每个都有独特的标识符,作为ASN.1定义数据类型的一部分]-[0a]
[SEQUENCE和SEQUENCE OF]-[有序数列,SEQUENCE里面的每个数值都可以是不同类型的,而SEQUENCE OF里是0个或多个类型相同的数据]-[10]
[SET和SET OF]-[无序数列,SET里面的每个数值都可以是不同类型的,而SET OF里是0个或多个类型相同的数据]-[11]
[NumericString]-[0-9以及空格]-[12]
[PrintableString]-[A-Z、a-z、0-9、空格以及符号 ()+,-./:=?]-[13]
[UTCTime]-[统一全球时间格式]-[17]
除了上述基本类型,ASN.1还定义了另外一些专用的数据类型,这里不再一一叙述。

openssl之ASN.1系列之2—ASN.1编码方法简介
参考资料:“Computer Network”,“A Layman s Guide to a Subset of ASN.1, BER, and DER”

ASN.1对象的编码是ASN.1标准的重要部分,目前,通常采用的是BER,而DER则是其一个子集。本文将对该编码方法作简单的介绍。
一个标准的ASN.1编码对象有四个域:对象标识域、数据长度域、数据域以及结束标志(可选,在长度不可知情况下需要,openssl中没有该标志)。
【对象标识域】
对象标识域有两种形式,低Tag数字(Tag值在0到30)和高Tag数字(Tag值大于30)形式。
低Tag数字形式只有一个字节,包含三部分,从低位为1开始编号,8和7位是Tag类型,共有四种,分别是universal(00)、application(0 1)、context-specific(1 0)和private(11);第6位是0,表明编码类型是基本类型,第5-1位是Tag值。
高Tag数字形式可以有两个或多个字节,第一个字节跟低Tag数字形式一样,但低5位值全为1,而在后续的第二个和其后的字节中给出Tag值,这些字节都只使用了低7位为数据位,最高位都设为0,但最后一个字节的最高位设为1,采用高位优先,经可能少的数字原则。
【数据长度域】
数据长度域也有两种形式,短形式和长形式。
短形式的数据长度域只有一个字节,第8位为0,其它低7位给出数据长度。
长形式的数据长度域有2到127个字节。第一个字节的第8位为1,其它低7位给出后面该域使用的字节的数量,从该域第二个字节开始给出数据的长度,基于256,高位优先。
【数据域】
数据域给出了具体的数据值。该域的编码对不同的数据类型不一样,这里就不在一一详述了,有兴趣的可以参看参考资料。

【一个编码例子】
下面是SSLDocument给出的对一个对象进行DER编码的例子,更多的例子可以参看本文给出的参考资料。
例子使用的对象是ASN.1定义的BIT STRING类型的对象,其编码的步骤如下:
1.对位串使用”0”进行填补,使其长度为8的整数倍(如果已经是整数倍,则不需要进行填补);
2.计算填补的位数并写下来,成为数据内容的第一个字节;
3.写入填补后的位串,高位字节优先。这些数据跟前面的一个字节组成数据内容的全部字节;
4.在这些数据前面加上一个头字节,这个字节定义如下(编号是从低位为1开始):
第8、7位:00(universal类型)
第 6 位 :0(表明是基本类型,有限长度的编码)
第5-1位:0x03(表明是BIT STRING)
这个字节定义了对象标识域;
5.然后在对象标识域字节和数据字节之间加入下面计算的定义的字节:
计算有多少字节的数据内容(对象标识域数据除外),如果少于127字节,那么就定义一个字节如下:
第8位:0
第7-1位:数据内容的字节数量
如果数据内容的字节数量大于127,就需要定义两个或多个字节,其中,第一个字节的定义如下:
第8位:1
第7-1位:该域后面还有多少字节
其后的字节是数据内容的字节数量,每字节基于256,高位优先
下面是一个实际的数据例子:
位串: 01000100111011
1.补齐两个0在后面,成为8的整数倍,得到 0100010011101100 ;
2. 02 作为第一个数据内容的字节;
3. 44 ec 作为其余的数据内容的字节;
4. 03 作为前面的对象标识字节;
5.因为BIT STRING的tag值3<=127,所以只有一个字节的长度域 03 ;
那么得到的这个位串的DER编码就是03 03 02 44 ec,其中,第一个字节是对象标识域,第二个字节是数据长度域,其他为数据域。

openssl之ASN.1系列之3—ASN.1函数概述和结构
【ASN.1函数库概述】
因为X509相关的协议都是基于ASN.1和DER编码的,所以openssl提供了一组函数,这些函数可以读取DER编码的对象,并将它们转换成openssl能够处理的内部格式;这些函数也可以将openssl里定义的C格式的对象结构转换成DER编码的对象。此外,该系列还提供了一些对这些对象进行比较、读取和设定指定值的函数。该系列函数还包括了一些签名函数,这是因为在签名之前,有些对象需要进行DER编码。

下面对ASN.1函数库中重要的数据结构做简单的介绍。
【ASN1_CTX】
该结构用来在ASN1处理过程中维护跟踪各种相关变量,其定义如下:
typedef struct asn1_ctx_st
{

unsigned char *p;
int eos;
int error;
int inf;
int tag;
int xclass;
long slen;
unsigned char *max;
unsigned char *q;
unsigned char **pp;
int line;
} ASN1_CTX;
参数p是工作字符指针,其最大长度由参数max指定;
eos是indefinite编码模式的结束标识标志;
error是错误代码;
inf值为0x20代表constructd模式,为0x21代表indefinite模式;
tag是最后取得的对象的tag值;
xclass是最后取得的对象的类型;
slen是最后取得的对象的长度;
line变量在出错处理的时候使用。
【ASN1_OBJECT】
该结构用来保存一个ASN1对象,其定义如下:
typedef struct asn1_object_st
{
char *sn,*ln;
int nid;
int length;
unsigned char *data;
int flags;
}ASN1_OBJECT;
nid是openssl内部定义的每个数字对象的独特标识码;
sn是对象的简称;
ln是对象的长名或小写名;
data是相应对象的数据,
length是该data字段的长度,
flags是一个释放标志。
【ASN1_STRING】
该结构是openssl里一个很基本的ASN.1对象结构,Openssl里定义的很多类型的对象都是采用该结构的,他们包括ASN1_INTEGER、ASN1_BIT_STRING、ASN1_OCTET_STRING、ASN1_PRINTABLESTRING、ASN1_T61STRING、ASN1_IA5STRING、ASN1_UTCTIME、ASN1_GENERALIZEDTIME、ASN1_GENERALSTRING、ASN1_UNIVERSALSTRING和ASN1_BMPSTRING。该结构的定义如下:
typedef struct asn1_string_st
{
int length;
int type;
unsigned char *data;
long flags;
} ASN1_STRING;
type参数指明对象的类型;
data参数是对象的数据,
length指定了其长度;
flags值跟type有关,一般来说,在BIT_STRING对象中使用。
【ASN1_TYPE】
该结构可以保存任意类型的ASN.1对象,其定义如下:
typedef struct asn1_type_st
{
int type;
union
{
char *ptr;
ASN1_BOOLEAN boolean;
ASN1_STRING * asn1_string;
ASN1_OBJECT * object;
ASN1_INTEGER * integer;
ASN1_ENUMERATED * enumerated;
ASN1_BIT_STRING * bit_string;
ASN1_OCTET_STRING * octet_string;
ASN1_PRINTABLESTRING * printablestring;
ASN1_T61STRING * t61string;
ASN1_IA5STRING * ia5string;
ASN1_GENERALSTRING * generalstring;
ASN1_BMPSTRING * bmpstring;
ASN1_UNIVERSALSTRING * universalstring;
ASN1_UTCTIME * utctime;
ASN1_GENERALIZEDTIME * generalizedtime;
ASN1_VISIBLESTRING * visiblestring;
ASN1_UTF8STRING * utf8string;
ASN1_STRING * set;
ASN1_STRING * sequence;
} value;
} ASN1_TYPE;
其中,参数type指定了对象的类型。
【ASN1_METHOD】
该结构包含了指向一组函数的指针,这些函数定义了在openssl内部结构和DER编码对象之间进行格式转换的功能,还定义了分配和释放一个结构的功能。其定义如下:
typedef struct asn1_method_st
{
int (*i2d)();
char *(*d2i)();
char *(*create)();
void (*destroy)();
} ASN1_METHOD;
i2d指向的函数将内部格式转换成DER编码格式;
d2i指向的函数将DER编码的对象转换成内部结构;
create指向的函数给新对象分配内存;
destroy指向的函数释放对象的内存。
例如,在文件x_x509.c里,X509对象的METHOD结构初始化如下:
static ASN1_METHOD meth=
{
(int (*)()) i2d_X509,
(char ()())d2i_X509,
(char ()())X509_new,
(void (*)()) X509_free
};

ASN1_METHOD *X509_asn1_meth()
{
return(&meth);
}
【ASN1_HEADER】
该结构只在Netscape格式的证书里使用了(参考apps/x509.c文件),其定义如下:
typedef struct asn1_header_st
{
ASN1_OCTET_STRING *header;
char *data;
ASN1_METHOD *meth;
} ASN1_HEADER;

除了上述介绍的基本结构外,ASN.1相关的结构还有几个,但这些因为不是特别通用,在这里不再作介绍,有兴趣可以看文件x509.h。

openssl之ASN.1系列之4—ASN.1对象的构造和释放

因为每种ASN.1对象都有相应的数据结构,所以openssl也提供了一系列创建和释放这些对象的函数。事实上,基本的函数并不多,很多函数是在基本的函数上提供的宏定义,主要是为了方便用户使用。
基本的对象构造和释放函数定义如下(crypto\asn1\asn1.h):
ASN1_OBJECT * ASN1_OBJECT_new(void );
void ASN1_OBJECT_free(ASN1_OBJECT *a);
ASN1_OBJECT * ASN1_OBJECT_create(int nid, unsigned char *data,int len,const char *sn, const char *ln);
ASN1_STRING * ASN1_STRING_new(void);
void ASN1_STRING_free(ASN1_STRING *a);
ASN1_STRING * ASN1_STRING_type_new(int type );
ASN1_HEADER * ASN1_HEADER_new(void );
void ASN1_HEADER_free(ASN1_HEADER *a);
ASN1_VALUE * ASN1_item_new(const ASN1_ITEM *it);
void ASN1_item_free(ASN1_VALUE *val, const ASN1_ITEM *it);

【ASN1_OBJECT】
该系列的new和free函数分别完成了ASN1_OBJECT对象的创建和释放。在创建ASN1_OBJECT对象的时候,该函数给对象分配内存空间,并将结构内所有指针类型的变量值都设为NULL,nid和长度都初始化为0,并将flags设置为ASN1_OBJECT_FLAG_DYNAMIC,返回创建的对象的指针,如果失败,返回NULL。对象释放的时候,free函数将所有对象成员的内存和自身的内存释放,并将lenght设置为0。
除了使用ASN1_OBJECT_new创建ASN1_OBJECT对象,还可以使用ASN1_OBJECT_create函数创建对象,该函数根据给定的参数创建一个ASN1_OBJECT对象。其中,nid是对象的独特标识NID;data为对象的数据,len指定了data有效数据的长度;sn是对象的简称;ln是对象的完整名字或小写名字;该函数将falgs标志设为ASN1_OBJECT_FLAG_DYNAMIC|ASN1_OBJECT_FLAG_DYNAMIC_STRINGS|ASN1_OBJECT_FLAG_DYNAMIC_DATA。该函数返回一个对象结构体,而不是指针。

【ASN1_STRING】
ASN1_STRING_type_new函数根据给定的参数type创建并返回一个ASN1_STRING对象指针。事实上,该函数不管type是什么,都是创建一个ASN1_STRING对象,然后将成员data初始化为NULL,flags和length初始化为0。唯一有区别的就是令成员变量type的值为参数type的值,目前支持的type值如下:
V_ASN1_BIT_STRING
V_ASN1_INTEGER
V_ASN1_ENUMERATED
V_ASN1_OCTET_STRING
V_ASN1_T61STRING
V_ASN1_PRINTABLESTRING
V_ASN1_VISIBLESTRING
V_ASN1_IA5STRING
V_ASN1_UTCTIME
V_ASN1_GENERALIZEDTIME
V_ASN1_GENERALSTRING
V_ASN1_UNIVERSALSTRING
V_ASN1_BMPSTRING
V_ASN1_UTF8STRING
ASN1_STRING_new函数是调用返回了ASN1_STRING_type_new函数实现的,其type参数为V_ASN1_OCTET_STRING。上述两个函数成功执行返回一个ASN1_STRING的指针,否则返回NULL。ASN1_STRING_free函数释放了用上述两个函数创建ASN1_STRING对象,没有返回值。

【ASN1_HEADER】
这两个函数用于处理Netscape格式的证书和私钥对象。事实上,new函数创建了一个ASN1_HEADER对象,并调用了ASN1_STRING_type_new函数,使用type参数为V_ASN1_OCTET_STRING对该对象的header成员变量进行初始化,并将meth和data设置为NULL,返回ASN1_HEADER对象的指针。free函数释放该类型对象,没有返回值。
【ASN1_VALUE】
这两个函数一般已经不在使用,只是为了兼容以前的版本而保留了下来。这里不再作介绍。
【基于上述基本函数的宏定义函数】
基于上述的基本函数,尤其是ASN1_STRING_type_new函数,openssl提供了很多为了方便用户使用的宏定义,这些宏定义的形式是如下述形式:
构造函数:M_对象名_new()
释放函数:M_对象名_free(a)
由于函数较多,具体可以参考asn1.h文件,这里不再一一列出了。
为了实现在Openssl内部对象结构和标准的DER编码对象之间格式的转换,OpenSSL定义了一组完成该功能的函数,这些函数基本上是以i2d(内部->DER)和d2i(DER->内部)开头的。跟其他各个系列的函数一样,OpenSSL虽然提供了针对各种对象类型的函数,但是其基本的函数不多,而其他都是在这个基础上实现的宏定义。这些基本函数如下(crypto\asn1\asn1.h):
int i2d_ASN1_OBJECT(ASN1_OBJECT *a,unsigned char **pp);
ASN1_OBJECT * d2i_ASN1_OBJECT(ASN1_OBJECT **a,unsigned char **pp,long length);
int i2d_ASN1_BOOLEAN(int a,unsigned char **pp);
int d2i_ASN1_BOOLEAN(int *a,unsigned char **pp,long length);
int i2d_ASN1_bytes(ASN1_STRING *a, unsigned char **pp, int tag, int xclass);
ASN1_STRING * d2i_ASN1_bytes(ASN1_STRING **a, unsigned char **pp,long length, int Ptag, int Pclass);
ASN1_STRING * d2i_ASN1_type_bytes(ASN1_STRING **a,unsigned char **pp,long length,int type);
int i2d_ASN1_SET(STACK *a, unsigned char **pp,int (*func)(), int ex_tag, int ex_class, int is_set);
STACK* d2i_ASN1_SET(STACK *a, unsigned char **pp, long length,char(func)(), void (*free_func)(void ),int ex_tag, int ex_class);

【i2d系列函数总体介绍】
i2d系列函数将一个内部的结构(c语言结构体)转换成DER编码的对象。参数a是一个指向一个结构体的指针,参数pp是一个指向并创建的DER编码字符串对象指针的指针。调用成功完成后,pp指针将被指向新生成的DER字符串的结束位置,并返回该字符串的长度。所以参数pp可以被相同的函数多次调用,以处理多个对象,并将这些对象存储成一个长DER编码的字符串。如果参数pp为NULL,则仅仅返回有效数据的长度。这种性质可以在首次调用的时候用来决定要分配的字符串空间的长度,例如下面的例子:
len=i2d_my_favorite_type(a,NULL);
if ((string=(char *)malloc(len)) == NULL) complain…
i2d_my_favorite_type(a,&string);
如果a为NULL,则返回0。

【d2i系列函数总体介绍】
d2i系列函数将一个DER编码的对象转换为一个内部的结构(c语言结构体)。参数a是一个指向一个结构体指针的指针,用来存放转换好的内部结构对象;参数pp是一个指向DER编码字符串对象指针的指针,参数length是*pp里面有效数据的长度。如果a为NULL,则仅仅将内部结构对象作为返回值返回;如果a不为NULL而*a为NULL,则将为*a分配内存并存储生成的内部结构对象。如果调用失败,返回NULL。调用成功完成后,*pp将被重置到*pp+length的位置,并返回生成的内部结构对象的地址。所以pp可以被d2i函数多次调用,以处理多个DER编码的对象。出错返回NULL,如果*a不为NULL,那么它指向的结构会被释放。一般来说,只有在内存分配失败或者DER编码数据有误的情况下才会出错。
【ASN1_OBJECT】
i2d_ASN1_OBJECT函数将一个内部结构ASN1_OBJECT的对象a转换成DER编码的对象;d2i_ASN1_OBJECT函数则是将参数pp里DER编码的对象数据转换成一个ASN1_OBJECT结构的对象。其参数a,pp和length跟上面介绍的是一样意义的。
【ASN1_BOOLEAN】
i2d_ASN1_BOOLEAN函数将一个内部结构ASN1_BOOLEAN的对象a转换成DER编码的对象;d2i_ASN1_BOOLEAN函数则是将参数pp里DER编码的对象数据转换成一个ASN1_BOOLEAN结构的对象。其参数a,pp和length跟上面介绍的是一样意义的。
【i2d_ASN1_bytes】

openssl之ASN.1系列之6—编码转换函数i2d和d2i(二)

作者:DragonKing(Eric Wang)
Mail: [email protected]
版权声明:未经作者授权,本文不能在任何商业性质的出版物或网站上进行转载
发布网站:http://openssl.126.com
OpenSSL版本:openssl-0.9.7

【ASN1_bytes】
int i2d_ASN1_bytes(ASN1_STRING *a, unsigned char **pp, int tag, int xclass);
ASN1_STRING * d2i_ASN1_bytes(ASN1_STRING **a, unsigned char **pp,long length, int Ptag, int Pclass);
这两个函数其实是相对来说比较底层的函数,一般不直接使用他们,而是使用基于他们的宏定义函数。
可以看到,跟其他i2d和d2i函数一样,这两个函数也有参数a和pp,其含义跟前面介绍是相同的。
i2d_ASN1_bytes函数中的参数tag是定义的对象的tag值,可能的值包括在下面:

define V_ASN1_UNDEF -1

define V_ASN1_EOC 0

define V_ASN1_BOOLEAN 1

define V_ASN1_INTEGER 2

define V_ASN1_NEG_INTEGER (2 | V_ASN1_NEG)

define V_ASN1_BIT_STRING 3

define V_ASN1_OCTET_STRING 4

define V_ASN1_NULL 5

define V_ASN1_OBJECT 6

define V_ASN1_OBJECT_DESCRIPTOR 7

define V_ASN1_EXTERNAL 8

define V_ASN1_REAL 9

define V_ASN1_ENUMERATED 10

define V_ASN1_NEG_ENUMERATED (10 | V_ASN1_NEG)

define V_ASN1_UTF8STRING 12

define V_ASN1_SEQUENCE 16

define V_ASN1_SET 17

define V_ASN1_NUMERICSTRING 18

define V_ASN1_PRINTABLESTRING 19

define V_ASN1_T61STRING 20

define V_ASN1_TELETEXSTRING 20

define V_ASN1_VIDEOTEXSTRING 21

define V_ASN1_IA5STRING 22

define V_ASN1_UTCTIME 23

define V_ASN1_GENERALIZEDTIME 24

define V_ASN1_GRAPHICSTRING 25

define V_ASN1_ISO64STRING 26

define V_ASN1_VISIBLESTRING 26

define V_ASN1_GENERALSTRING 27

define V_ASN1_UNIVERSALSTRING 28

define V_ASN1_BMPSTRING 30

参数xclass可能的值如下:

define V_ASN1_UNIVERSAL 0x00

define V_ASN1_APPLICATION 0x40

define V_ASN1_CONTEXT_SPECIFIC 0x80

define V_ASN1_PRIVATE 0xc0

这两个参数的信息用来填写DER编码的标识字节,没有这些信息,函数就会只是对ASN1_STRING对象进行没有意义或错误的编码。比如你如果不给ASN1_INTEGER类型对象一个tag值,那么返回的编码就是一堆没有意义的垃圾代码。
d2i_ASN1_bytes函数比其他d2i函数也多两个参数,Ptag和Pxclass,是两个指针,分别是用来存放从标识字节读出的上述tag和class值。没有这两个值,函数就只是将DER对象作为ASN1_STRING对象进行处理,确定不了类型。该函数返回一个ASN1_STRING对象。

【ASN1_type_bytes】
ASN1_STRING * d2i_ASN1_type_bytes(ASN1_STRING **a,unsigned char **pp,long length,int type);
本函数也是一个底层的函数。相比于其他d2i函数,本函数多了一个参数type,该参数有效的值如下:

define B_ASN1_NUMERICSTRING 0x0001

define B_ASN1_PRINTABLESTRING 0x0002

define B_ASN1_T61STRING 0x0004

define B_ASN1_TELETEXSTRING 0x0008

define B_ASN1_VIDEOTEXSTRING 0x0008

define B_ASN1_IA5STRING 0x0010

define B_ASN1_GRAPHICSTRING 0x0020

define B_ASN1_ISO64STRING 0x0040

define B_ASN1_VISIBLESTRING 0x0040

define B_ASN1_GENERALSTRING 0x0080

define B_ASN1_UNIVERSALSTRING 0x0100

define B_ASN1_OCTET_STRING 0x0200

define B_ASN1_BIT_STRING 0x0400

define B_ASN1_BMPSTRING 0x0800

define B_ASN1_UNKNOWN 0x1000

define B_ASN1_UTF8STRING 0x2000

define B_ASN1_UTCTIME 0x4000

define B_ASN1_GENERALIZEDTIME 0x8000

该函数完成的功能跟d2i_ASN1_bytes函数是一样的,不过,如果从DER对象解码得到的对象类型跟type参数给定的类型不一致,那么,就会出错返回NULL。如果没有这个参数,就不能确定得到对象的类型,只是返回一个ASN1_STRING的指针。

openssl之ASN.1系列之7—编码转换函数i2d和d2i(三)

作者:DragonKing(Eric Wang)
Mail: [email protected]
版权声明:未经作者授权,本文不能在任何商业性质的出版物或网站上进行转载
发布网站:http://openssl.126.com
OpenSSL版本:openssl-0.9.7

【ASN1_SET】
int i2d_ASN1_SET(STACK *a, unsigned char **pp,int (*func)(), int ex_tag, int ex_class, int is_set);
STACK* d2i_ASN1_SET(STACK *a, unsigned char **pp, long length,char(func)(), void (*free_func)(void ),int ex_tag, int ex_class);
这两个函数对ASN1_SET和ASN1_SEQUENCE类型的对象进行内部对象和DER编码之间的转换。

1.i2d_ASN1_SET函数
参数a和pp是跟前面介绍的意义是一样的。参数ex_tag的值可能如如下:
V_ASN1_SET
V_ASN1_SEQUENCE
参数ex_class的值可能如下:
V_ASN1_UNIVERSAL
V_ASN1_CONTEXT_SPECIFIC
参数func指向一个i2d_*形式的函数指针,它用来对参数a的STACK中的每一个元素进行内部结构向DER编码转换的操作。这个STACK包括了将要组成整个SET或SEQUENCE的ASN1结构的有序集合。例如,如果STACK中包含了一些列X509_ATTRIBUTE对象,那么你可以调用下面的函数得到DER编码的SET对象:
i2d_ASN1_SET(sk,&p,i2d_X509_ATTRIBUTE,V_ASN1_SET,V_ASN1_UNIVERSAL)

2.d2i_ASN1_SET函数
改函数跟其它d2i函数完成的功能是一样的,其参数a、pp、ex_tag和ex_class跟上面的函数里同名参数的意义是一样的。参数func是d2i_*形式的函数,用来对DER编码的SET或SEQUENCE对象中的每个对象进行DER解码并创建相应的ASN1结构,然后将创建的对象结构存放在STACK参数a中,返回该对象的指针。如果出错,该函数返回NULL。
【ASN1_STRING相关的宏定义函数】
前面我们介绍过,OpenSSL里面许多ASN1对象类型的底层其实是ASN1_STRING类型的宏定义,所以OpenSSL提供了一系列宏定义函数对这些类型相应的tag和class进行正确的DER编解码处理,这些函数的形式一般为:
M_i2d_*
M_d2i_*
他们的参数也跟前面介绍过的同名参数意义一样。为了方便查找,把这些宏定义函数列出如下:

define M_i2d_ASN1_OCTET_STRING(a,pp) i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_OCTET_STRING,V_ASN1_UNIVERSAL)

define M_i2d_ASN1_PRINTABLE(a,pp) i2d_ASN1_bytes((ASN1_STRING *)a,pp,a->type,V_ASN1_UNIVERSAL)

define M_d2i_ASN1_PRINTABLE(a,pp,l) d2i_ASN1_type_bytes((ASN1_STRING **)a,pp,l, B_ASN1_PRINTABLE)

define M_i2d_DIRECTORYSTRING(a,pp) i2d_ASN1_bytes((ASN1_STRING *)a,pp,a->type,V_ASN1_UNIVERSAL)

define M_d2i_DIRECTORYSTRING(a,pp,l) d2i_ASN1_type_bytes((ASN1_STRING **)a,pp,l, B_ASN1_DIRECTORYSTRING)

define M_i2d_DISPLAYTEXT(a,pp) i2d_ASN1_bytes((ASN1_STRING *)a,pp,a->type,V_ASN1_UNIVERSAL)

define M_d2i_DISPLAYTEXT(a,pp,l) d2i_ASN1_type_bytes((ASN1_STRING **)a,pp,l,B_ASN1_DISPLAYTEXT)

define M_i2d_ASN1_PRINTABLESTRING(a,pp) i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_PRINTABLESTRING,V_ASN1_UNIVERSAL)

defineM_d2i_ASN1_PRINTABLESTRING(a,pp,l) (ASN1_PRINTABLESTRING*)d2i_ASN1_type_bytes((ASN1_STRING **)a,pp,l,B_ASN1_PRINTABLESTRING)

define M_i2d_ASN1_T61STRING(a,pp) i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_T61STRING,V_ASN1_UNIVERSAL)

define M_d2i_ASN1_T61STRING(a,pp,l) (ASN1_T61STRING )d2i_ASN1_type_bytes((ASN1_STRING *)a,pp,l,B_ASN1_T61STRING)

define M_i2d_ASN1_IA5STRING(a,pp) i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_IA5STRING,V_ASN1_UNIVERSAL)

define M_d2i_ASN1_IA5STRING(a,pp,l) (ASN1_IA5STRING )d2i_ASN1_type_bytes((ASN1_STRING *)a,pp,l,B_ASN1_IA5STRING)

define M_i2d_ASN1_GENERALSTRING(a,pp) i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_GENERALSTRING,V_ASN1_UNIVERSAL)

define M_d2i_ASN1_GENERALSTRING(a,pp,l) (ASN1_GENERALSTRING )d2i_ASN1_type_bytes((ASN1_STRING *)a,pp,l,B_ASN1_GENERALSTRING)

define M_i2d_ASN1_UNIVERSALSTRING(a,pp) i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_UNIVERSALSTRING,V_ASN1_UNIVERSAL)

defineM_d2i_ASN1_UNIVERSALSTRING(a,pp,l) (ASN1_UNIVERSALSTRING*)d2i_ASN1_type_bytes((ASN1_STRING **)a,pp,l,B_ASN1_UNIVERSALSTRING)

define M_i2d_ASN1_BMPSTRING(a,pp) i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_BMPSTRING,V_ASN1_UNIVERSAL)

define M_d2i_ASN1_BMPSTRING(a,pp,l) (ASN1_BMPSTRING )d2i_ASN1_type_bytes((ASN1_STRING *)a,pp,l,B_ASN1_BMPSTRING)

define M_i2d_ASN1_VISIBLESTRING(a,pp) i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_VISIBLESTRING,V_ASN1_UNIVERSAL)

define M_d2i_ASN1_VISIBLESTRING(a,pp,l) (ASN1_VISIBLESTRING )d2i_ASN1_type_bytes((ASN1_STRING *)a,pp,l,B_ASN1_VISIBLESTRING)

define M_i2d_ASN1_UTF8STRING(a,pp) i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_UTF8STRING, V_ASN1_UNIVERSAL)

define M_d2i_ASN1_UTF8STRING(a,pp,l) (ASN1_UTF8STRING )d2i_ASN1_type_bytes((ASN1_STRING *)a,pp,l,B_ASN1_UTF8STRING)

下面是有关的OpenSSL内部结构和ASN.1定义的对象的对照列表:
ASN1_BIT_STRING BIT STRING
ASN1_BMPSTRING BMPString
ASN1_GENERALIZEDTIME GeneralizedTime
ASN1_GENERALSTRING GeneralString
ASN1_INTEGER INTEGER
ASN1_OCTET_STRING OCTET STRING
ASN1_OBJECT OBJECT IDENTIFIER
ASN1_PRINTABLESTRING PrintableString
ASN1_T61STRING T61String
ASN1_IA5STRING IA5String
ASN1_TYPE 上面提到任意一种类型,包括SEQUENCE和SET类型
ASN1_UNIVERSALSTRING UniversalString
ASN1_UTCTIME UTCTime

openssl之ASN.1系列之8—编码转换函数i2d和d2i(四)

作者:DragonKing(Eric Wang)
Mail: [email protected]
版权声明:未经作者授权,本文不能在任何商业性质的出版物或网站上进行转载
发布网站:http://openssl.126.com
OpenSSL版本:openssl-0.9.7

前面把i2d和d2i函数介绍的差不多了,烦闷异常,下面举一个例子,是SSLeay Document提供的,在本系列介绍DER编码的时候,也用过这个例子数据的。权作对上面说的函数的复习。
假设我们的要进行编解码处理的对象是一个位串(BIT STRING),其二进制值为
01000100111011
其DER编码是:
03 03 02 44 ec
其中,第一个字节(03)是对象标识字节,第二个字节(03)是长度,第3到5个字节(02 44 ec)是数据内容。
【从DER编码转换为内部C格式(d2i_ASN1_BIT_STRING函数)的过程】
1.分配一个BIT STRING的C内部结构,该内部结构如下:
typedef struct asn1_bit_string2_st
{
int length;
int type;
unsigned char *data;
} ASN1_BIT_STRING;
2.调用ASN1_get_object完成提取数据长度信息、对象标识符等工作;同时还完成了对象标识符检查工作,以保证对象是ASN1_BIT_STRING,并给type赋值;
3.然后将数据长度放到length变量,内容数据放到data变量(除了每个字节第一位特殊位除外)。大家知道,特殊位的最后一位是0(参考前面的DER编码介绍),并返回结构体的指针,完成d2i的工作。
需要注意的是:
a.该函数过程没有将数据右移,也就是说,数据里保存了0补丁的数据;之所以这样是因为C结构里的位串数据总是成8的倍数位的。
b.数据不是NULL结束的字符串,这点要特别注意。
【从内部C格式转换为DER编码(i2d_ASN1_BIT_STRING函数)的过程】
1.第一步,如果数据不是8位的倍数,需要进行补丁;当然这里正好是8的倍数,不用补丁了。
2.使用参数V_ASN1_BIT_STRING调用函数ASN1_put_object函数,设置和输出对象标识字节,计算和输出数据长度,输出数据编码后的内容。
需要注意的是:
a.在完成最后一个字节输出后,pp指针会被执行最后一个输出字节的下一个字节。这样,就可以通过多次调用该i2d函数来完成对一个包含多个ASN1对象的DER编码的顺序处理。
b.如果pp是NULL,那么不会输出任何数据。但是函数会返回要输出的数据的长度,这样可以用来在正式调用函数之前给一个空指针分配适当的内存空间,如下:
int len;
unsigned char *bytes,*p;
len=i2d_X509(x,NULL); /* 取得x对象ASN1的DER编码的数据长度*/
if ((bytes=(unsigned char *)malloc(len)) == NULL)
goto err;
p=bytes;
i2d_X509(x,&p);
可以看到,上述的程序里,使用&p而不是&bytes作为i2d_X509的输入参数,这是因为p指针在调用函数i2d_X509后会增加为p+len值,是变化的。但是为了进一步对编码后的数据进行处理,比如保存或输出到文件等,就必须保留原始位置的指针bytes,这在实际应用中几乎是一定需要这样处理的。

猜你喜欢

转载自blog.csdn.net/liao20081228/article/details/77411474