xiedeacc
发布于 2023-12-31 / 7 阅读
0
0

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 和 r estricted character string不能使用 construted
  • Set中的组成值需与 ASN.1定义的顺序一致

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

将着重分析 X.509的编码解码在 libmbedtls中的源码分析,相当硬核。

作者:SummmmmerNeru
链接:https://www.jianshu.com/p/81e6e73b5c81
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


评论