ASN.1編解碼標準X.690
書接上文,我們有提到X.509
格式的證書通常是使用ASN.1
的格式編碼的。那么ASN.1
是個啥,如何進行編碼解碼呢。這篇文章主要用來解讀ASN.1
的標準,學習成為解碼人柱力。
ASN.1與TLV
先看看wiki對于ANS.1
格式的描述:
在電信和計算機網絡領域,ASN.1(Abstract 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
中的Type
。BER
中將其分解成三段如下:
* 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-specific
。Context-specific
它只能使用在SEQUENCE
、SET
和CHOICE
類型的組件中,而且可以看到有一個關鍵字EXPLICIT
以及對應的關鍵字IMPLICIT
用于配合使用。而中括號中間的0
則標識了對應的Tag number
, 注意該數字僅在該上下文中(即單獨SEQUENCE
、SET
和CHOICE
組成的內部)生效。
其他兩種使用很少,也不推薦使用,多用于某些應用和公司內部的私有協議,此處不討論了。
再看看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
標準實際上是有超過0x0F
種Tag
的,此時該如何編碼呢?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之后將使用長模式:第一個字節的最高比特bit8
置1
,剩下的比特將指示該長度將由多少個字節來表示,同樣的將通過大端編碼的方式形成一串比特串進行編碼。以上一篇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
布爾類型非常簡單,Type
的Class
為Universal
,非嵌套,Tag number
為0x1
, Length
為0x1
。當Value
字節為全0
時記為false
, 否則為true
;下表表示一種布爾類型的編碼,共3字節。
Identifier | Length | Contents |
---|---|---|
0x01 | 0x01 | 0xFF |
X.509
中有一個標準的拓展字段用了該布爾類型:
/* X509v3 Basic Constraints: critical
CA:FALSE */
/* encode: */
[01] [01] [FF]
Integer
Type
的Class
為Universal
,非嵌套,Tag number
為0x02
, 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
Type
的Class
為Universal
,可以為primitive
也可以為constructed
. Tag number
為0x3
. Length
為編碼的字節長度;其content
的組成,第一個字節將編碼了最后一個字節中填充的0bit的個數, 故解碼時也應該去除掉最后一個自己的對應比特數的0,該字節被稱為Unused bits
;通常可以使用多個primitive
的Bitstring
分段,組成一個constructed
的Bitstring
, 此時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 number
為0x3
,且因為最小以字節為單位,無需第一個字節標識填充信息。通常在X.509
使用在Extension
中,標準下ASN.1的extnValue格式為Octetstring
,同時由于此類型支持其他類型的嵌套,以之前證書authorityKeyIdentifier
的extnValue
為例:
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
Type
的Class
為Universal
,primitive
. Tag number
為0x5
,Length
一定為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
定義的協議都是基于該類型定義的. Type
的Class
為Universal
, Constructed
, Tag number
為0x10
. 故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
編碼中的Identifier
和Length
,本例中被簽名段將忽略掉前兩個中括號的內容, 即第三個中括號中的第一個中括號[[30] [82 03 B0] [A0 03 02 01 02 02 11 00 ...]
為被簽名段.
Set/Set of
Type
的Class
為Universal
, Constructed
, Tag number
為0x11
. 故Identifier
通常為0x31
。與Sequence
基本一致,區別在于此類型在定義、解析和編碼時無需強制順序,沒有前后順序,各成員體等價。在X.509
中Name
類型包含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
Type
的Class
為Universal
, Primitive
和Constructed
都可能, 通常為Primitive
,Tag number
為0x17
。其編碼為ascii
碼下的YYMMDDhhmm[ss]Z
或YYMMDDhhmm[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共同開發的一種廣泛使用的機制來命名任何類型的對象、概念或事物,具有一個全局明確的名稱和一個長生命周期的名稱。
Type
的Class
為Universal
, Primitive
, Tag number
為0x6
.
先看該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)
-
c
在X.509
典型有certificateExtension(29)
;attributeType(4)
-
d
在X.509
典型有countryName(6)
;organizationName(10)
當然還能延續。其中最典型的OID
是算法ID,以上面證書為例子:sha256WithRSAEncryption
的OID
為1.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
編碼方式非常特殊:
-
a
和b
將作為第一個字節進行編碼,編碼結果為: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
編碼規則的學習之后,CER
和DER
就很好理解了,他們對BER
多加了一些可用性的限制。先看兩者共有的與X.509
相關的限制。
Common Restrictions
-
Bool FALSE
所有比特應被編碼1,即該字節為0xFF
-
Bitstring
不應該出現需要補充比特的情況,即Unused bits
應該為0 -
Sequence
/Set
中被設置為默認值的成員不應編碼 -
UTCTime
僅有Z
模式
CER
-
identifier
為Construted
時,長度應該指定為indefinite
(此處有疑問,.cer
格式下的證書constructed
也有使用definite
長度的) -
identifier
為primitive
時,長度應該指定為最短字節:例如長度0x10
在BER
中可以編碼為0x81 0x10
,CER
增加了該限制
DER
- 長度只能使用
definite
模式 -
bitstring
,octetstring
和 restricted character string
不能使用construted
-
Set
中的組成值需與ASN.1
定義的順序一致
基本上看完此文就可以化身人肉解碼器了,最后推薦一個工具:ASN.1 Editor, 很好用。
將著重分析X.509
的編碼解碼在libmbedtls
中的源碼分析,相當硬核。