CSP學習之ASN.1編碼(一)

1、定義:
是定義抽象數據類型的標準。
是用於描述數據的表示、編碼、傳輸、解碼的靈活記法。
它提供一套正式、無歧義和精確的規則,以描述獨立於特定計算機硬件的對象結構。
標準的ASN.1編碼規則有其基本編碼規則BER,規範編碼規則CER,惟一編碼規則DER,壓縮編碼規則PER,和XML編碼規則。
2、編碼規則
一般ASN.1編碼採用TLV格式。
由三部分組成:    TAG--頭字節  LEN--長度 VAL--數據
(1)頭字節(TAG)
一般位於ASN.1編碼的開始,由3部分組成: 類別  結構化位   原始類型
最高位第7和8位表示 類別
    第六位表示 結構化位
    第5-1位表示 原始類型
類別位 :描述數據將要解釋的上下文。
位8 

位7 

類 

通用(universal) 

應用(Application) 

上下文特定 

專用 

結構化位 :表示一個給定的編碼是不是相同類型的多種編碼的結構化。
當一個應用程序在邏輯上是一個元素,但並非一次就包含全部組件的元素進行編碼時,結構化位就十分有效。結構化元素也是容器類型所必須的,由於在邏輯上,他們只是其餘元素的集合。
結構化元素有本身的頭字節和長度字節,以後是元素各個要素組件的單獨編碼,也就是說,從他們自身來看,這些要素組件式獨立的可解碼ASN.1數據類型。
咱們假設除容器外,其餘全部數據類型的結構化位都爲0
原始類型 : 
ASN.1提供了一些基本的預約義的數據類型:
UNIVERSAL    0    保留給編碼規則使用
UNIVERSAL    1    布爾類型
UNIVERSAL    2     整數
UNIVERSAL    3    二進制字符串類型    位串
UNIVERSAL    4    八進制字符串類型    八位位組串
UNIVERSAL    5    空類型
UNIVERSAL    6    對象標識符類型
UNIVERSAL    7    對象描述符類型
UNIVERSAL    8    外部類型和類型實例
UNIVERSAL    9    實數類型
UNIVERSAL    10    枚舉類型
UNIVERSAL    11    嵌入的pdv類型
UNIVERSAL    12    UTF8字符串類型
UNIVERSAL    13    相關對象標識符類型
UNIVERSAL    14-15    保留
UNIVERSAL    16    序列和類型序列
UNIVERSAL    17   集合和類型的集合
UNIVERSAL    18-22,25-30    字符串類型
UNIVERSAL    23-24    時間類型
(2)長度(Len)
根據編碼的實際長度,ASN.1定義了兩種長度編碼。根據編碼的長度和環境,能夠用定長或者非定長方式進行編碼,並且還能夠進一步分割成短編碼或者長編碼。在基本編碼規則中(BER)編碼器能夠自由的選擇短編碼或者長編碼,但前提是可以徹底表示元素的長度。可是 DER編碼規則規定必須選擇能夠徹底表示元素長度的最短編碼 。編碼後的長度並不包含ASN.1頭字節和長度字節,以減小負載。
編碼的第一字節表示短編碼仍是長編碼
最高位表示編碼是短的仍是長的,而低7位則造成一個長度當即數
a 短編碼
在短編碼中,負載的長度必須小於128個字節,長度當即數域用來表示負載的長度,這也是編碼大小限制的來源。它是對全部長度小於128個字節的強制性編碼。
b 長編碼
在長編碼中,定義了附加的抽象數據來對長度進行編碼,它僅適用於全部長度大於128字節的負載,在這種狀況下,長度當即數域的存儲是爲了表示負載長度所需的字節數,進一步講,它表示了爲了編碼負載的長度須要多少字節,長度必須以big-endian格式進行編碼。
如,對長度47310(即0xB8CE)進行編碼,比128大,因此採用長編碼格式,實際長度須要用2個字節來表示。所以第一個字節則爲 0x80|0x02 即0x82.而後用big-endian格式存儲長度值,即 0x82 B8 CE
這種方式容許對長度高達2^1016位的對象進行編碼。
根據DER編碼規則,負載長度值的長度必須爲最小,所以全部1字節是無效的。通常來講,對於長編碼,能夠放心的假設長度當即數大於4個字節是無效的,由於不多有密碼協議在一個數據包中交換大於4G的數據。
3、數據類型
4.1    布爾類型
根據BER編碼規則,真值的編碼能夠是任何非零值,可是DER要求真值編碼爲0xFF
布爾值        編碼
真        0x01 01 FF
假        0x01 01 00
4.2    整數型
表示一個有符號的任意精度的標量,它的編碼是可移植的,平臺無關的。
存儲的實際數值分爲字節大小的數字,而且以big-endian格式進行存儲,例如,對於變量
    x = 256^k *x(k) + 256^(k-1)*x(k-1) + .... + 256^0 * x0
進行編碼,八位位組{xk, ... , x0}將以遞減順序從xk到x0進行存儲。編碼過程規定對於正整數,第一個字節的最高位必須爲0
所以,假設第一個字節大於127(如49468(0xC13C) 0xC1>0x7F),看上去其編碼應該是0x02 02 C1 3C, 但他的最高位爲1,因此應該當作負數,最簡單有效的辦法是用前段零字節填充,即49468編碼爲0x02 03 00 C1 3C。
負數的編碼並不簡單,這個過程須要找到一個最小的256的冪,使它比要編碼的數的絕對值還大,好比-1555進行編碼,比1555還大的256的最小冪爲256^2 = 65536 而後,把這兩個數相加以獲得2的補碼錶示形式,本例爲63981。實際編碼的整數是這個和。
所以在這個例子中,-1555的ASN.1編碼爲0x02 02 F9 ED,而後對整數的編碼使用兩個附加準則,以減少輸出的大小。
第一個八位組的全部位和第二個八位組的第8位必須爲:
    不全爲1
    不全爲0
整數解碼至關容易,若是第一個最高位爲0,被編碼的值是正的且負載時絕對標量值。若是最高位爲1,則被編碼的值是負的且要從編碼值中減去下一個最大的256的冪。
尤爲要注意128和-128編碼的不一樣,他們都賦值爲0x80但正值須要0x00前綴來區分
值 

編碼 

0x02 01 00 

0x02 01 01 

0x02 01 02 

127 

0x02 01 7F 

128 

0x02 02 00 80 

-1 

0x02 01 FF 

-128 

0x02 01 80 

-32768 

0x02 02 80 00 

1234567890 

0x02 04 49 96 02 D2 

4.3    位串類型
位串(BIT STRING)類型用來以可移植形式表示位數組。除了ASN.1頭部以外,還有一個附加的頭部用來表示填充數據。
這些位以下進行編碼,將第一位放到第一個負載字節的最高位。下一位存儲到第一個負載字節的第7位,以此類推。例如對位串{1,0,0,1,0,1,1,0}, 編碼爲0x8E
編碼的第一個字節指定造成一個完整的字節所需填充的位數,例如位串{1,0,0,1}將轉變爲{1,0,0,1,0,0,0,0},其填充數量爲4。若是位串爲空,填充數爲0。有效的填充長度範圍爲0-7.
負載的長度包括填充數字節和已編碼的位。
位串{1,0,0,1}的編碼爲0x03 02 04 90 。應該注意負載的長度爲0x02 而不是0x01,由於咱們把填充字節做爲負載的一部分。
解碼器經過計算  8*負載長度 - 填充數 來獲得存儲輸出所須要的位數。
4.4    八位位組串類型
除了八位位組串(OCT STRING)是保存字節(八位位組)數組以外,它和位串類型很類似,這種類型編碼至關簡單,像任何其餘類型同樣對頭部進行編碼,而後直接將八位位組賦值過去就能夠了。
例如對八位位組串(FE, ED, 6A, B4)進行編碼,首先存儲類型爲0x04 接着長度爲0x04 而後就是字節自己 0xFE ED 6A B4
4.5    空類型
空(NULL)類型其實是「佔位符」, 它是含有空白選項的選擇修改器所特有的。
例如考慮下列的序列(SEQUENCE)
    MyAccount ::= SEQUENCE{
        Name        IA5String,
        Group        IA5String,
        Credentials     CHOICE{
            rsaKey        RSAPublicKey,
            passwdHash    OCTST STRING,
            none        NULL
        },
        LastLogin     UCTIME,
        ...
    }
在這個結構中帳號的證書應該是一個RSA密鑰或者一個密碼散列值或者什麼都沒有。若是不存在這個空類型,編碼器必須選擇二者之一,而且將其餘類型指定爲空類型。
空類型編碼爲0x05 00 它在DER編碼中沒有負載,然而,從技術上說,在BER編碼中必須忽略它的負載。
4.6    對象標識符類型
對象標識符(OBJECT IDENTIFIER, OID)類型用層次的形式來表示標準規範,標識符樹經過一個點分十進制符號來定義。這個符號以組織、子部分而後是標準的類型和各自的子標識開始。
例如MD5的散列算法的OID爲1.2.840.113549.2.5
這個OID分爲 iso (1)  member-body(2) US(840) rasdsi(113549) digestAlgorithm(2) md5(5)
當看到這個標識時,解碼程序(並非解碼器自己)可以認識到這是個MD5散列算法。
由於這個緣由,OID在公鑰算法標準中很流行,它指出證書綁定了那種散列算法。但OID不只僅侷限於散列算法,一樣也有公鑰算法,分組算法和操做模式的OID。他們是一種高效且可移植的表示數據包中所選算法的形式,並不須要用戶來指出算法類型的「魔幻解碼」。
這種點分十進制是很容易理解的,但下面兩種規則除外:
一、第一部分的範圍必須是0<=x<=3
二、若是第一部分小於2,那麼第二部分必須小於40
除此以外,其他部分可使任何正的無符號數,他們的大小一般小於32位,但並不必定都是這樣。
對各個部分的編碼有點複雜,但仍然是能夠容易理解的。前兩部分若是定義爲x和y,那麼他們講合成一個字40x+y,其他部分單獨做爲一個字進行編碼。
每一個字首先被分割爲最少數量的沒有頭零數字的7位數字。這些數字以big-endian格式進行組織,而且一個接一個的組合成字節。除了編碼的最後一個字節外,其餘全部每一個字節的最高位都爲1。例如30 331分割成7位的數字以後爲{1,108,123}
128^2*1 + 128^1*108 + 128^0*123 = 30331
把他們應用於MD5則爲{42, 840, 113549, 2, 5}
進一步將其分割爲帶有最高位的7位數字,即{{0x24}, {0x86, 0x48}, {0x86, 0xF7, 0x0D},{0x02},{0x05}}
即 0x06 08 2A 86 48 86 F7 0D 02 05
4.7    序列和集合類型
序列(SEQUENCE)和單一序列(SEQUENCE OF)以及相應的集合(SET)和單一集合(SET OF)類型叫作「結構」類型或者叫簡單容器。他們是一種用來把相關數據元素收集爲一個獨立的可解碼元素的簡單方法。
編碼的內容應由ASN.1序列類型定義列表中的全部數據類型值的徹底編碼組成,而且按照他們出現的順序進行編碼,除非這些類型被可選(OPTIONAL)或者默認(DEFAULT)關鍵字修改器所引用。
結構化的意思是位6必須設置,這使得序列頭字節的值變成由0x10到0x30。結構化編碼是一種簡單的嵌套編碼。以下序列:
    User ::= SEQUENCE{
        ID    INTEGER,
        Active    BOOLEAN
    }
當對值{32, TRUE}編碼時,首先給字節0x30代表這是一個結構化的序列。接着咱們給出負載的長度,即整數和布爾類型的編碼長度,共六個字節即0x06。而後開始結構化部分。將整數編碼爲0x02 01 20 布爾類型編碼爲0x01 01 FF所以整個編碼爲 0x30 06 02 01 20 10 01 FF。在ASN.1文檔中,他們使用空白來代表編碼的屬性。
0x30 06     
        02 01 20 
        01 01 FF
若是使用下面的嵌套結構,這種符號表示特別有用。
Account ::= SQEUENCE{
    User SEQUENCE{
        Name    PrintableString,
        Group    PrintableString,
        Credential     SEQUENCE{
            PasswdHash     OCTST STRING,
            RSAKey    RSAPublickey    OPTIONAL
    },
    LastOn        UTCTIME,
    Valid        BOOLEAN
}
當給定序列{{"tom","users",{0x01 02 03 04 05 06 07 08}},"060416180000Z", TRUE},它將被編碼爲以下格式:
Account    0x30 2C
User        0x30 18
Name            13 03 74 6F 6D
Group            13 05 75 73 65 74 75
Credential            30 0A
PasswdHash            04 08 01 02 03 04 05 06 07 08
LastOn        17 0D 30 36 30 34 31 36 31 38 30 30 30 30 5A
Valid        01 01 FF
和openssl庫一同安裝的openssl命令行程序提供了一種把DER編碼的文件轉化爲可閱讀的到處的簡單辦法。
你能夠和這個第三方已知的正確的進行比較。能夠經過以下命令
    openssl    asn1parse -inform der -in $INFILE -i            ¥INFILE是文件名
(2)集合
集合石一種相似於序列的結構化類型,但他的頭字節是0x31而不是0x30,且其成員編碼的順序並非集合定義的順序。嚴格來說,對BER規則來講,發送者是能夠判斷出順序的,意思是說,若是沒有將集合的順序事先發給接受者,那麼,集合不能包含兩個相同的類型。在DER編碼規則中,是按照類型值的順序進行升序排列,若是兩個元素有相同的類型,則由已提交集合的原始順序決定「加時賽規則‘,即這個重複類型的第一次出現爲」勝者「。
對於前面的序列,其集合編碼爲
User ::= SET{
    ID    INTEGER,
    Active    BOOLEAN
}
當對值{32, TRUE}進行編碼時,咱們首先給出字節0x31來表示這是一個結構化的集合。其長度爲6。根據DER規則,首先將他們的類型進行排序,由於布爾值類型爲0x01,整數類型爲0x02因此布爾值做爲第一個。所以編碼完成後的結構爲0x31 06 01 01 FF 02 01 20
對於下面的集合:
User ::= SET{
    ID    INTEGER,
    Active    BOOLEAN,
    LogCount    INTEGER
}
在這個例子中,對實例{32, TRUE, 1023}編碼以頭字節0x31開始,長度爲0A 接下來對布爾類型編碼,ID和LogCount爲相同類型,可是ID首次出現,因此接下來存儲它的編碼0x02 01 20 最後是LogCount編碼 0x02 02 3F FF完整編碼爲0x31 0A 01 01 FF 02 01 20 02 02 3F FF
4.8    可打印字符串和IA5String類型
可打印字符串類型(PrintableString)和IA5String類型定義了一種獨立於本地代碼頁和字符集定義,在任何平臺上均可以將ASCII字符串編碼爲可讀字符串的可移植方法。
可打印字符串編碼對象是ASCII集合的一個有限子集,這個子集包括32(空格),39(單引號),40-41, 43-58, 61, 63, 以及65-122 這些範圍以外的數值都是無效的而且應該報錯,可打印字符串的意思是在不改變顯示文字流的狀況下,可以在大多數的終端下打印出來的字符。
IA5String類型編碼對象時ASCII集合中的大多數值,它包括NUL,BEL,TAB,NL,LF,CR以及在32和126之間的ASCII碼值,包括32和126。一般,在沒有過濾的狀況下使用TTY來顯示IA5String是不安全的。由於它容許那些已編碼的數據作一些諸如清屏,替換字符之類的事情。
可打印字符串的頭字節爲0x13
IA5String的頭字節爲0x16
例如hello world 編碼爲  0x13 0B 48 65 6D 6D 6F 20 57 6F 72 6D 64
4.9    世界協調時類型
世界協調時(UTCTIME)定義了一種相對於GMT時間的時間標準(以日期)編碼。
從X.690的2002草案開始,全部的UTC編碼都應該使用」YYMMDDHHMMSSZ「這種格式,分別表示年 月 日 時 分 秒
Z 遺留自初始UTCTIME,若是沒有Z,那麼就容許兩種附加組"[+/-]hh 'mm'" 其中hh和mm分別爲於GMT的時差和分差(正值或者負值)。若是有Z,則時間是以Zulu或者GMT時間表示的。
採用IA5String類型表示,如2013年7月4號 11:33:28 表示爲
        0x17 0D 31 33 30 37 30 34 31 31 33 33 32 38 5A 


相關文章
相關標籤/搜索