Openssl ASN.1 说明二(i2d d2i) 分享

 openssl之ASN.1系列之5---编码转换函数i2d和d2i(一)

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

为了实现在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: wzhah@263.net
版权声明:未经作者授权,本文不能在任何商业性质的出版物或网站上进行转载
发布网站: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: wzhah@263.net
版权声明:未经作者授权,本文不能在任何商业性质的出版物或网站上进行转载
发布网站: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: wzhah@263.net
版权声明:未经作者授权,本文不能在任何商业性质的出版物或网站上进行转载
发布网站: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,这在实际应用中几乎是一定需要这样处理的。
  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Linux C编程中,使用OpenSSL添加自定义ASN.1 OID并对数据进行编码和解码可以通过以下步骤实现: 1. 定义自定义OID:在OpenSSL中,可以通过OBJ_create函数定义自定义OID。例如,定义一个自定义OID为1.2.3.4.5,可以使用以下代码: ```c #include <openssl/obj_mac.h> #include <openssl/objects.h> #define MY_OID "1.2.3.4.5" int main() { int nid = OBJ_create(MY_OID, "myOID", "My OID"); if (nid == NID_undef) { printf("Error creating OID\n"); return 1; } printf("Custom OID created with NID=%d\n", nid); return 0; } ``` 2. 编码数据:使用ASN.1编码器对数据进行编码。在OpenSSL中,可以使用ASN1_item_pack函数实现。例如,编码一个字符串"Hello World",可以使用以下代码: ```c #include <openssl/asn1.h> int main() { ASN1_OCTET_STRING *str = ASN1_OCTET_STRING_new(); ASN1_OCTET_STRING_set(str, "Hello World", strlen("Hello World")); unsigned char *buf = NULL; int len = ASN1_item_pack(str, ASN1_ITEM_rptr(ASN1_OCTET_STRING), &buf); if (len < 0) { printf("Error encoding data\n"); return 1; } printf("Encoded data: "); for (int i = 0; i < len; i++) { printf("%02x ", buf[i]); } printf("\n"); OPENSSL_free(buf); ASN1_OCTET_STRING_free(str); return 0; } ``` 3. 解码数据:使用ASN.1解码器对数据进行解码。在OpenSSL中,可以使用ASN1_item_unpack函数实现。例如,解码上一步中编码的数据,可以使用以下代码: ```c #include <openssl/asn1.h> int main() { unsigned char buf[] = {0x04, 0x0b, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64}; ASN1_OCTET_STRING *str = ASN1_item_unpack(buf, sizeof(buf), ASN1_ITEM_rptr(ASN1_OCTET_STRING)); if (!str) { printf("Error decoding data\n"); return 1; } printf("Decoded data: %s\n", str->data); ASN1_OCTET_STRING_free(str); return 0; } ``` 在实际使用中,需要根据具体的需求进行调整和优化。同时,需要注意安全问题,防止因为错误或恶意数据导致的安全漏洞。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值