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

 openssl之ASN.1系列之5---編碼轉換函數i2d和d2i(一)

作者:DragonKing(Eric Wang)
Mail: [email protected]
版權聲明:未經作者授權,本文不能在任何商業性質的出版物或網站上進行轉載
發佈網站: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: [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,這在實際應用中幾乎是一定需要這樣處理的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章