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中的源码分析,相当硬核。

你可能感兴趣的:(X.509系列(二):ASN.1编解码标准X.690)