X.509系列(二):ASN.1編解碼標準X.690

ASN.1編解碼標準X.690

書接上文,我們有提到X.509格式的證書通常是使用ASN.1的格式編碼的。那么ASN.1是個啥,如何進行編碼解碼呢。這篇文章主要用來解讀ASN.1的標準,學習成為解碼人柱力。

ASN.1與TLV

先看看wiki對于ANS.1格式的描述:

在電信和計算機網絡領域,ASN.1Abstract Syntax Notation One) 是一套標準,是描述數據的表示、編碼、傳輸、解碼的靈活的記法。它提供了一套正式、無歧義和精確的規則以描述獨立于特定計算機硬件的對象結構。

看起來很厲害,再看看其類別有哪些:

ASN.1本身只定義了表示信息的抽象句法,但是沒有限定其編碼的方法。各種ASN.1編碼規則提供了由ASN.1描述其抽象句法的數據的值的傳送語法(具體表達)。標準的ASN.1編碼規則有基本編碼規則(BER,Basic Encoding Rules)、規范編碼規則(CER,Canonical Encoding Rules)、唯一編碼規則(DER,Distinguished Encoding Rules)、壓縮編碼規則(PER,Packed Encoding Rules)XML編碼規則(XER,XML Encoding Rules)。

很顯然,這種編碼方法非常多。但是不要慌,大部分密碼相關標準用到的編碼方式都是BER/CER/DER,而后面兩種則是對BER增加限制后的產物。至于PER/XER,至少在筆者目前工作中還沒有接觸到過,本文就不進行介紹了。

為什么要把前三者歸為一類呢,原因很簡單,這三種編碼方式是一種典型TLV的編碼方法。TLV:Type-Length-Value, 是一種各類通訊協議中都非常常見的編碼手段,他將一段數據分解使用Type描述了數據類型,用Length描述的數據長度,最后的Value表示了真正的data。其優點也非常明顯,他可以將一段數據以二進制的格式編碼,大量壓縮了編碼導致的報文體積膨脹的消耗,同時由于編碼簡單,解析速度也非常迅速。其結構很簡單,通常如下:

Type | Length | Value

以一個手機號碼的編碼為例18570917612

先看Type,假設用一個字節表示Type,如0x00表示手機號碼,0x01表示固定電話號碼,0x02表示傳真號等等。當然此時還可以區分國家號、區號等等,都可以通過Type進行拓展,這里我們簡單只考慮號碼的類型。那顯然可以編碼為0x00, 編碼的類型通過也稱為Tag

Length就比較簡單,通常標識字節數,而手機號碼通常是使用ASCII碼來標識,則每一個手機號碼數字被編碼為一個字節,總計就是11,長度被編碼為0x0B.

最后Value就是數據主體,使用ASCII編碼則為:0x31 0x38 0x35 0x37 0x30 0x39 0x31 0x37 0x36 0x31 0x32

最終這個手機號碼將被編碼為(去掉0x,直接看真實的二進制下表示):

Tag Length Value
00 0B 31 38 35 37 30 39 31 37 36 31 32

當然,TLV是支持嵌套的,即Value同樣是一個TLV編碼的數據。后面也將會看到。

X.690

既然ASN.1是一個通用的,和通信與密碼強相關的編碼記法,當然是需要一個明確的標準。而X.690是當前比較公認的標準。主要也是定義了BER/CER/DER這三種編碼格式。下文的解析也是遵循該標準的(08/2015)版本 。

BER

作為基礎編碼規則,我們首先學習它。BER通常把一個字節叫做Octets,而從低到高的比特位分別被叫做bit1 - bit8,和通常計算機對于最低比特稱作bit0略有不同。

先貼一下之前的X.509證書, 用于后文實例解析中的對比:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            bc:01:41:05:22:d8:cc:7f:02:00:00:00:00:79:64:7f
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = US, O = Google Trust Services, CN = GTS CA 1O1
        Validity
            Not Before: Aug 26 08:14:23 2020 GMT
            Not After : Nov 18 08:14:23 2020 GMT
        Subject: C = US, ST = California, L = Mountain View, O = Google LLC, CN = www.google.com
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:bc:1c:aa:96:6f:6f:99:48:79:56:61:4b:7f:ff:
                    dc:39:08:3a:d4:4d:e2:d8:87:80:af:3d:18:5e:71:
                    2d:ce:09:70:57:39:38:5f:2a:ee:a8:35:f4:3a:86:
                    86:5a:1d:c7:31:32:1b:8d:ac:d0:46:ad:c3:fc:a5:
                    d3:18:36:68:ab
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature
            X509v3 Extended Key Usage: 
                TLS Web Server Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Subject Key Identifier: 
                AF:32:A8:9D:20:98:F3:FD:14:41:FE:F4:C4:74:47:7C:D1:6C:81:B1
            X509v3 Authority Key Identifier: 
                keyid:98:D1:F8:6E:10:EB:CF:9B:EC:60:9F:18:90:1B:A0:EB:7D:09:FD:2B        
        Authority Information Access: 
            OCSP - URI:http://ocsp.pki.goog/gts1o1core
            CA Issuers - URI:http://pki.goog/gsr2/GTS1O1.crt

        X509v3 Subject Alternative Name: 
            DNS:www.google.com
        X509v3 Certificate Policies: 
            Policy: 2.23.140.1.2.2
            Policy: 1.3.6.1.4.1.11129.2.5.3

        X509v3 CRL Distribution Points: 

            Full Name:
              URI:http://crl.pki.goog/GTS1O1core.crl

        CT Precertificate SCTs: 
            Signed Certificate Timestamp:
                Version   : v1 (0x0)
                Log ID    : 5E:A7:73:F9:DF:56:C0:E7:B5:36:48:7D:D0:49:E0:32:
                            7A:91:9A:0C:84:A1:12:12:84:18:75:96:81:71:45:58
                Timestamp : Aug 26 09:14:24.417 2020 GMT
                Extensions: none
                Signature : ecdsa-with-SHA256
                            30:45:02:20:77:F3:D6:8B:51:4F:88:71:16:73:ED:36:
                            2F:64:F4:77:3E:92:D3:CE:97:1F:1C:53:FA:4E:FB:5B:
                            D7:0A:4C:D6:02:21:00:9F:B9:FE:F1:F3:1C:0D:CF:20:
                            30:B1:1C:0A:01:65:AD:67:90:1F:B5:33:90:8D:49:4D:
                            2B:ED:1D:90:28:A1:6B
            Signed Certificate Timestamp:
                Version   : v1 (0x0)
                Log ID    : 07:B7:5C:1B:E5:7D:68:FF:F1:B0:C6:1D:23:15:C7:BA:
                            E6:57:7C:57:94:B7:6A:EE:BC:61:3A:1A:69:D3:A2:1C
                Timestamp : Aug 26 09:14:24.367 2020 GMT
                Extensions: none
                Signature : ecdsa-with-SHA256
                            30:45:02:21:00:F4:67:8E:8B:ED:3F:B2:D4:EA:72:EB:
                            53:F1:52:57:98:D6:63:0E:C0:6B:68:46:CE:F3:AD:25:
                            52:AD:12:83:27:02:20:05:CA:04:76:D6:4F:2A:E5:D3:
                            96:85:79:A2:F3:85:29:9E:89:30:00:A7:20:99:2D:F7:
                            C9:56:3C:4E:5D:5C:CF
Signature Algorithm: sha256WithRSAEncryption
     7a:9a:76:80:c9:39:13:8e:60:b1:93:5d:99:49:1b:71:b5:b2:
     2e:bd:4b:db:56:f0:eb:fa:f4:ae:93:f6:1b:dd:b0:df:2a:81:
     08:fc:4a:a9:ec:b1:ae:09:f0:fa:40:7b:b8:be:dc:08:4c:46:
     32:99:29:f8:13:6b:72:af:16:79:63:d3:3f:76:56:57:19:78:
     91:86:f8:7a:ee:26:67:98:dc:5e:e4:00:f5:87:a0:01:21:9d:
     cf:e5:9f:02:f3:2a:fd:0e:fd:78:af:2e:20:29:77:35:e2:c6:
     30:ee:ef:be:f2:bb:26:02:52:a2:2d:27:78:ce:a9:8e:39:d0:
     a2:74:90:11:c5:92:58:3c:7a:88:1d:c7:5a:56:d4:1a:01:00:
     c3:9d:98:6f:41:02:1f:cb:e2:4d:99:6a:5c:d9:0f:c0:88:08:
     15:c5:26:90:a2:a4:15:f6:71:e2:fe:a9:98:dc:40:2a:71:c1:
     11:aa:00:73:52:24:74:aa:ae:72:55:2f:0d:31:b7:00:bb:1f:
     87:4d:f5:05:ad:ff:7a:93:e0:cf:86:a5:1d:1b:7d:41:fa:10:
     99:3b:00:7c:c9:dd:a9:52:5c:06:72:86:96:e7:05:97:77:12:
     2f:26:bb:dc:65:c4:48:4d:9c:82:4b:7d:69:27:3f:85:00:2e:
     b1:5d:8d:dc

Identifier Octets

標識字節,可以理解為TLV中的TypeBER中將其分解成三段如下:

 * Bit  8     7   6   5          1
 *     +-------+-----+------------+
 *     | Class | P/C | Tag number |
 *     +-------+-----+------------+

先看Class段,兩個比特bit7-bit8,定義了四種類型:

Class Bit 8 Bit 7
Universal (0x00) 0 0
Application (0x40) 0 1
Context-specific (0x80) 1 0
Private (0xC0) 1 1

其中Universal是最常用的,屬于Native的類型都將屬于該段。而Context-specific也比較多見,密碼學相關編碼較多。通常當字段的類型非標準類型時都將使用該類型作為Identifier Octets的高位。以X.509證書中標準ASN.1版本號為例,Type被描述為A0

Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }

version         [0]  EXPLICIT Version DEFAULT v1,
/* Tag is 'A0': 0x80 | 0x20 | 0 */

此時A0的高位比特即為1和0,對應Context-specificContext-specific它只能使用在SEQUENCESETCHOICE類型的組件中,而且可以看到有一個關鍵字EXPLICIT以及對應的關鍵字IMPLICIT用于配合使用。而中括號中間的0則標識了對應的Tag number, 注意該數字僅在該上下文中(即單獨SEQUENCESETCHOICE組成的內部)生效。

其他兩種使用很少,也不推薦使用,多用于某些應用和公司內部的私有協議,此處不討論了。

再看看P/C, 對應1個比特:0 = Primitive(0x00); 1 = Constructed(0x20)。通常該位標識后續的Value是一個原子值還是一個嵌套的TLV. 例如INTEGER只能為Primitive,而SEQUENCE 一定是Constructed;當然也有Both允許的類型,如BIT STRING,可能是嵌套的也可能是原子的,不過顯然嵌套的情況比較少,X.509證書暫時沒找到相關例子。

最后是最關鍵的Tag Number,使用了5個比特(bit1 - bit5)標識了最基本的原生類型,下表將解釋常用的類型:

Name Permitted Construction Decimal Tag number Hexadecimal Tag number
End-of-Content (EOC) Primitive 0 0
BOOLEAN Primitive 1 1
INTEGER Primitive 2 2
BIT STRING Both 3 3
OCTET STRING Both 4 4
NULL Primitive 5 5
OBJECT IDENTIFIER Primitive 6 6
UTF8String Both 12 C
SEQUENCE and SEQUENCE OF Constructed 16 10
SET and SET OF Constructed 17 11
PrintableString Both 19 13
T61String Both 20 14
IA5String Both 22 16
UTCTime Both 23 17
GeneralizedTime Both 24 18
UniversalString Both 28 1C
BMPString Both 30 1E

具體的在X.509所需的編碼字段將在實例中進行講解。需要特別提到的EOC通常用在TLV串結尾標識數據已結束,屬于一種可選編碼方式,X.509中未使用。

一個問題在于,此時Tag Number是無法超過0x0F的,而根據X.690標準實際上是有超過0x0FTag的,此時該如何編碼呢?ASN.1使用0b11111作為Tag Number進行拓展,此時接下來的字節為真實的Tag Number,其中當最高位比特bit8為標識位,0標識為最后一個字節,1標識為中間字節,最后進行比特串的拼接,大端編碼。類似下面的標識:

  * Leading Octet Bit  8       7  6   5         1
  *                   +-------+-----+---------+
  *                   | Class | P/C | 0b11111 |
  *                   +-------+-----+---------+
  * 2nd Octet    Bit    8  7           1
  *                   +---+------------+
  *                   | 1 | 0bxxxxxxx  |
  *                   +---+-----+------+
  ...
  * Last Octet   Bit    8  7           1
  *                   +---+------------+
  *                   | 0 | 0bxxxxxxx  |
  *                   +---+-----+------+
  
  * Example for tag number 0x80: 0b10000000
  * binary : [class:P/C:11111][1:0000001][0:0000000] for 3-byte

不過這種情況比較少見,在密碼學標準中基本不會出現。

Length octets

標識了編碼內容的長度,屬于比較簡單的字段。與Tag Number編碼方式類似,也被區分為了短模式與長模式:

短模式下,bit8將為0,剩下的比特將作為實際數據長度。例如上面的手機號碼長度11,將被編碼為0b00001011。顯然這種情況下長度是有限的:不能超過127(0b01111111)個byte.

超過127之后將使用長模式:第一個字節的最高比特bit81,剩下的比特將指示該長度將由多少個字節來表示,同樣的將通過大端編碼的方式形成一串比特串進行編碼。以上一篇X.509的證書簽名段為例:

/* 
 * Type 0x03 : Bit String
 * Length: 
 * 1st Octet: 0b10000002(0x82): bit-8 = 1 stands for long form; 
 * bit1-7 = 0x2 encode the number of subsequent octets in the length octets, bit-7 is the most significant bit; Note: 0xFF can't be used.
 * 2nd and 3rd Octet: bit 8 of the first subsequent octet is the most significant bit;
 * 0b00000001|00000001(0x01 0x01) for content length 0x101.
 */
signatureValue       BIT STRING
(Encoded as: [03] [82 01 01] [Signature])

這里有個疑問:為啥RSA-2048為啥出來需要0x101個字節,多了一個字節啊。其實看一下就知道,編碼的最高字節為0x00。至于這個的用法如何將在后續對不同Type的內容做編碼時講到。

此外ASN.1提供了另外一種靈活的編碼方式:即當Length被編碼為0b10000000(0x80)時,長度被定義為不定模式(Indefinite),后續Contents octets的尾部則必須加上上節提到的EOC,標識內容結束。

Contents octets

編碼內容,這個部分將對X.509系列所需常用的類型編碼方式進行詳述

EOC

EOC模式一定為以下格式:

Identifier Length Contents
0x00 0x00 NULL
Boolean

布爾類型非常簡單,TypeClassUniversal ,非嵌套,Tag number0x1, Length0x1。當Value字節為全0時記為false, 否則為true;下表表示一種布爾類型的編碼,共3字節。

Identifier Length Contents
0x01 0x01 0xFF

X.509中有一個標準的拓展字段用了該布爾類型:

/* X509v3 Basic Constraints: critical
                   CA:FALSE */
 /* encode: */
 [01] [01] [FF] 
Integer

TypeClassUniversal ,非嵌套,Tag number0x02, Length為編碼的字節長度,content為大數編碼的整數,即第一個字節的最高比特為MSB,直接以上一章證書中間的serialNumber, 很簡單,標識了一個大數88比特、11字節的大數:

CertificateSerialNumber  ::=  INTEGER
serialNumber         CertificateSerialNumber

/* Serial Number:
               bc:01:41:05:22:d8:cc:7f:02:00:00:00:00:79:64:7f */
[02] [11] [00 BC 01 41 05 22 D8 CC 7F 02 00 00 00 00 79 64 7F]

其中數字的比較與普通數字比較沒有差別,如上述數字與[02] [10] [BC 01 41 05 22 D8 CC 7F 02 00 00 00 00 79 64 7F]比較是相等的。

Bitstring

TypeClassUniversal,可以為primitive也可以為constructed. Tag number0x3. Length為編碼的字節長度;其content的組成,第一個字節將編碼了最后一個字節中填充的0bit的個數, 故解碼時也應該去除掉最后一個自己的對應比特數的0,該字節被稱為Unused bits;通常可以使用多個primitiveBitstring分段,組成一個constructedBitstring, 此時Length可以設置為不定長模式0x80. 通常不需要使用construct模式。看下面的例子:

/* If of type BIT STRING, the value '0A3B5F291CD'H */
/* Primitive mode encode: 04 stands for the last byte include 4 bit padding */
[03] [07] [[04] 0A 3B 5F 29 1C D0]

/* 
 * Constructed mode encode: 23 stands for 0x00(class) | 0x20(constructed) | 0x03(Tag number) 
 * The Bitstring consists of 2 sub-bitstrings: 0A3B, first byte 00 stands for 0 padding bits;
 * And 5F291CD0, first byte 04 last byte include 4 bit padding.
 */
[23] [0C] [[[03] [03] [[00] 0A 3B]] [[03] [05] [[04] 5F 29 1C D0]]]

解釋一下X.509里簽名段的編碼:

signatureValue       BIT STRING
/* 
 * Signature Algorithm: sha256WithRSAEncryption
 *    7a:9a:76:80:c9:39:13:8e:60:b1:93:5d:99:49:1b:71:b5:b2:
 *    2e:bd:4b:db:56:f0:eb:fa:f4:ae:93:f6:1b:dd:b0:df:2a:81:
 *    08:fc:4a:a9:ec:b1:ae:09:f0:fa:40:7b:b8:be:dc:08:4c:46: 
 *    ...
 */
/* The first byte 00 stands for 0 padding bits */
Encoded as: [03] [82 01 01] [[00] 7A 9A 76 80 C9 39 13 8E 60 B1 93 5D ...]
Octetstring

Bitstring基本類似,差別在于Tag number0x3,且因為最小以字節為單位,無需第一個字節標識填充信息。通常在X.509使用在Extension中,標準下ASN.1的extnValue格式為Octetstring,同時由于此類型支持其他類型的嵌套,以之前證書authorityKeyIdentifierextnValue為例:

Extension  ::=  SEQUENCE  {
        extnID      OBJECT IDENTIFIER,
        critical    BOOLEAN DEFAULT FALSE,
        extnValue   OCTET STRING  }
  
/* X509v3 Authority Key Identifier: 
                keyid:98:D1:F8:6E:10:EB:CF:9B:EC:60:9F:18:90:1B:A0:EB:7D:09:FD:2B */
[04] [18] [30 16 80 14 98 D1 F8 6E 10 EB CF 9B EC 60 9F 18 90 1B A0 EB 7D 09 FD 2B]
NULL

TypeClassUniversalprimitive. Tag number0x5Length一定為0. 不需要Content,即通常編碼固定為05 00; X.509中在AlgorithmIdentifier會出現,如本文的例子中:

AlgorithmIdentifier  ::=  SEQUENCE  {
     algorithm               OBJECT IDENTIFIER,
     parameters              ANY DEFINED BY algorithm OPTIONAL  }

/* parameters == NULL encoded in {} */
[30] [0D] [[[06] [09] [2A 86 48 86 F7 0D 01 01 0B]] {[05] [00]}] 
Sequence/Sequence of

許多使用ASN.1定義的協議都是基于該類型定義的. TypeClassUniversal, Constructed, Tag number0x10. 故Identifier通常為0x30。通過上一節就能看到,其實整張X.509證書就是一個sequence,它由 Version, CertificateSerialNumber, AlgorithmIdentifier等多個類型的字段組合而成。例子可以參考上面的AlgorithmIdentifier;同時可以看一下整張證書的例子:

   Certificate  ::=  SEQUENCE  {
        tbsCertificate       TBSCertificate,
        signatureAlgorithm   AlgorithmIdentifier,
        signatureValue       BIT STRING  }

/* Cert: length = 0x4C8 */
/* 
 * TBSCertificate and signatureAlgorithm are also SEQUENCEs with length 0x3B0 and 0x0D 
 * signatureValue is  BIT STRING with length 0x101, bits padding number is 0
 */
[30] [82 04 C8] [[[30] [82 03 B0] [A0 03 02 01 02 02 11 00 ...]] 
                 [[30] [0D] [30 0D 06 09 2A 86 48 86 ...]]
                 [[03] [82 01 01] [[00] 7A 9A 76 80 C9 39 13 8E 60 B1 93 5D 99 49 1B 71 ...]]]

注意:證書中被簽名的字段需要忽略掉Certificate編碼中的IdentifierLength,本例中被簽名段將忽略掉前兩個中括號的內容, 即第三個中括號中的第一個中括號[[30] [82 03 B0] [A0 03 02 01 02 02 11 00 ...] 為被簽名段.

Set/Set of

TypeClassUniversal, Constructed, Tag number0x11. 故Identifier通常為0x31。與Sequence基本一致,區別在于此類型在定義、解析和編碼時無需強制順序,沒有前后順序,各成員體等價。在X.509Name類型包含set類型.

Name ::= CHOICE { -- only one possibility for now --
    rdnSequence  RDNSequence }

RDNSequence ::= SEQUENCE OF RelativeDistinguishedName

RelativeDistinguishedName ::=
    SET SIZE (1..MAX) OF AttributeTypeAndValue

AttributeTypeAndValue ::= SEQUENCE {
    type     AttributeType,
    value    AttributeValue }

AttributeType ::= OBJECT IDENTIFIER

AttributeValue ::= ANY -- DEFINED BY AttributeType
   
/* The encoding of a choice value shall be the same as the encoding of a value of the chosen type. */
/* 
 * Issuer: C = US, O = Google Trust Services, CN = GTS CA 1O1
 * Sequence { 
 *  Set { 
 *      Sequence { 
 *          type OBJECT IDENTIFIER, value AttributeValue 
 *      }, 
 *      Sequence { 
 *          type OBJECT IDENTIFIER, value AttributeValue 
 *      }, ... }, 
 *  Set { ... } 
 * }
 */

/* Sequence 30 { set 31 { Sequence 30 {OID 06, Printable String 13 }} ... } */
  [30] [42] [[[31] [0B] [[30] [09] [[[06] [03] [55 04 06]] [[13] [02] [55 53]]]]] ... ]
UTCTime

TypeClassUniversal, PrimitiveConstructed都可能, 通常為PrimitiveTag number0x17。其編碼為ascii碼下的YYMMDDhhmm[ss]ZYYMMDDhhmm[ss](+|-)hhmm

YY表示年,其中如果YY < 50則年份為20YY年,否則為19YY年,如YY = 50則標識1950年;MM表示月份,DD表示日;hhmmss表示時分秒,其中ss是可選項。

Z則標識Zulu時間,而(+|-)hhmm則標識了與格林威治標準時間的時差, +標識標準時間提前,-標識推后。Z與時差不能共用。

看看X.509中的例子:

/* Validity
      Not Before: Aug 26 08:14:23 2020 GMT
      Not After : Nov 18 08:14:23 2020 GMT */

/* ascii code, print as 200826081423Z */
[17] [0D] [32 30 30 38 32 36 30 38 31 34 32 33 5A] 
/* ascii code, print as 201118081423Z */
[17] [0D] [32 30 31 31 31 38 30 38 31 34 32 33 5A] 
Restricted character string

標識一組收到限制的string類型,如NumericString/VisibleString/PrintableString等. X.509最主要使用的是PrintableString。以Set例子中的Name里可打印字符US為例,identifier = 0x13

 /* 
  * PrintableString 
  * length = 2
  * "US" : 0x55 0x53
  */
 [13] [02] [55 53]
Object identifier

簡稱OID,是一個用來編碼特殊意義字段的標準定義ID,屬于ITU-T和ISO/IEC共同開發的一種廣泛使用的機制來命名任何類型的對象、概念或事物,具有一個全局明確的名稱和一個長生命周期的名稱。

TypeClassUniversal, Primitive, Tag number0x6.

先看該OID解碼的格式,通常為a.b.c.d....(被稱為 dot notation). 其中每一個.將分割一個特殊意義的字符。

  • a中比較典型的有iso(1)joint-iso-itu-t(2)
  • b中比較典型的有member-body(2); identified-organization(3); ds(5); country(16)
  • cX.509典型有certificateExtension(29); attributeType(4)
  • dX.509典型有countryName(6); organizationName(10)

當然還能延續。其中最典型的OID是算法ID,以上面證書為例子:sha256WithRSAEncryptionOID1.2.840.113549.1.1.11; 對應{iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1) sha256WithRSAEncryption(11)}.

具體查詢OID含義可以查看網站http://oid-info.com,可以查到大部分符合標準定義下的OID的詳細描述,以及每個OID下的child OID,非常方便。具體實現X.509功能相關所需的OID定義可以在下一部分的libmbedtls源碼分析中看到oid.h中的定義。

content編碼方式非常特殊:

  • ab將作為第一個字節進行編碼,編碼結果為:a * 40 + b。舉個例子2.5的編碼結果為0x55 = 85 = 2 * 40 + 5
  • 剩下的每個字節的編碼相同,非常類似超過30之后的Tag number的編碼方式:首先被分割為最少數量的沒有頭零數字的7位數字. 這些數字以big-endian格式進行組織, 并且一個接一個地組合成字節. 編碼的最后一個字節j將為0,其他所有字節的最高位(位8)都為1。舉個例子840 = 0b1101001000,編碼為0b0000110 | 0b1001000,最后補充高位標記位0b10000110 | 0b01001000, 即0x86 0x48

看X.509中對sha256WithRSAEncryption的編碼:

/* 1.2.840.113549.1.1.11 */
/* 
 * 0x2A = 42 = 1 * 40 + 2: "1.2" 
 * 840 : 0x86 0x48
 * 113549 =  0b (0000110 | 1110111 | 0001101): 0b10000110 0b11110111 0b00001101 = 0x86 0xF7 0x0D
 * Remained is simple encode as short integer. 
 */
[06] [09] [2A 86 48 86 F7 0D 01 01 0B]

完成基礎類型的學習,我們返回去看對于X.509 Version的編碼就好理解了:

version  [0]  EXPLICIT Version DEFAULT v1,
Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }

/* 
 * EXPLICIT implies a context-specific(0x80) and construted by a integer; 
 * [0] stands for the tag number;
 * so identifier: 'A0': 0x80 | 0x20 | 0 , length: 0x03, content: type integer;
 * Interger tag 0x02, length 0x01, content 0x02;
 */
 [A0] [03] [[02] [01] [02]]

完成基礎的BER編碼規則的學習之后,CERDER就很好理解了,他們對BER多加了一些可用性的限制。先看兩者共有的與X.509相關的限制。

Common Restrictions

  • Bool FALSE所有比特應被編碼1,即該字節為0xFF
  • Bitstring不應該出現需要補充比特的情況,即Unused bits應該為0
  • Sequence/Set中被設置為默認值的成員不應編碼
  • UTCTime僅有Z模式

CER

  • identifierConstruted時,長度應該指定為indefinite(此處有疑問,.cer格式下的證書constructed也有使用definite長度的)
  • identifierprimitive時,長度應該指定為最短字節:例如長度0x10BER中可以編碼為0x81 0x10, CER增加了該限制

DER

  • 長度只能使用definite模式
  • bitstring, octetstring 和 restricted character string不能使用construted
  • Set中的組成值需與ASN.1定義的順序一致

基本上看完此文就可以化身人肉解碼器了,最后推薦一個工具:ASN.1 Editor, 很好用。

將著重分析X.509的編碼解碼在libmbedtls中的源碼分析,相當硬核。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。