Openssl 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文件,這里不再一一列出了。

原文鏈接:https://blog.csdn.net/jasenwan88/article/details/7718851


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM