ASN.1 学习

Oct 12, 2021

目录


简介


ASN.1 全称 Abstract Syntax Natation One. 是一个用来描述抽象类型抽象数据的语法. 类似于 XML, JSON 等, 主要用于编码数据以便于在网络中交换数据. 比如, X509 证书.

ASN.1 中定义了四种类型:

  • 简单类型 (Simple types): 用于编码基本的数据类型. 如 整形,字符串,二进制数据等.
  • 结构类型 (Structured types): 用于编码复杂数据类型. 如, X509 证书中的公钥.
  • 标记的类型 (Tagged types): 用于编码从其他几种类型引申而来的类型.
  • 其他类型 (Other types): CHOICE 或 Any.

ASN.1 中每个类型都对应与一个 class 和 非负的 tag number. 用来区别不同的 ASN.1 类型.


四种 class:


  • Universal: 用来表示在所有应用中都具有相同意义的类型. 这些类型定义在 X.208
  • Application: 用来表示针对于具体应用的类型. 在不同的应用中这些类型的意义往往不同
  • Private: 用来表示针对用于具体企业的类型
  • Context-specific: 用来表示针对于具体上下文该类型意义会变化的类型

列出这些类型仅供参考, 其在网络协议中往往应用较少. 因为具体的协议往往会规定某个数据的具体ASN.1结构. 对于每个数据的每个字段也都有严格的规定.


Tag Numbers:


Type Tag Number (decimal) Tag Number (hexadecimal)
INTEGER 2 02
BIT STRING 3 03
OCTET STRING 4 04
NULL 5 05
OBJECT IDENTIFIER 6 06
SEQUENCE and SEQUENCE OF 16 10
SET and SET OF 17 11
PrintableString 19 13
T61String 20 14
IA5String 22 16
UTCTime 23 17

下面我们详细介绍每一种类型. 我们的目标是通过本文的描述,我们可以手工的解析一个X509证书.

-----BEGIN CERTIFICATE-----
MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw
MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT
aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ
jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp
xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp
1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG
snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ
U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8
9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E
BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B
AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz
yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE
38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP
AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad
DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME
HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
-----END CERTIFICATE-----

常用数据类型


简单类型

Type Descrption
BIT STRING 由任意的 0 和 1 构成的字符串
IA5String 任意的 ASCII 字符串
INTEGER 任意的整型
NULL 表示空值
OBJECT IDENTIFIER 由一系列的整型序列组成
OCTET STRING 由八比特值构成的字符串, 类似于比特数组
PrintableString 由任意的可打印字符构成的字符串
T61String 由八比特字符构成的字符串
UTCTime GMT 时间值

结构类型

Type Descrption
SEQUENCE 一个有序的类型集合
SEQUENCE OF 一个给定类型的有序集合
SET 一个无序的类型集合
SET OF 一个给定类型的无序集合

结构类型中可以包含可选的部分, 可选部分也可以有默认值.


Basic Encoding Rules


Basic Encoding Rules 简称为 BER, 规定了如何将 ASN.1 值表示为比特数组.

针对不同给数据类型, BER 规定了如下三种编码方法: Primitive, definte-length encoding, Contructed, definite-length encoding, Constructed, indefinite-length encoding.

BER 编码包含以下部分:

  • Identifier octets: 由类型的 class 和 tag number 构成. 用来声明该类型是基本类型还是结构化类型.
  • Length octets: 对于定长编码方法, 该部分给定 content 的长度. 对于不定长编码方法, 该部分指明该类型是不定长的.
  • Contents octets: 对于基本类型, 这部分包含对应数据的字节表示. 对于复杂类型, 这部分包含了该数据的 BER 编码结果.
  • End-of-contents octets: 对于不定长编码方法, 该部分标识 content 的结束. 其他编码方法, 这部分省略.

下面我们学习一下上述三种编码方法:

  1. Primitive, definte-length encoding

    基本数据类型除字符串类型外均使用这种编码方式. 这种情况下我们的数据编码结果会是以下形式:

    在这里插入图片描述

    其中不包含 End-of-contents 部分, 因为这里我们使用定长编码.

    对于 Identifier, 有两种形式:

    low tag number form: 这种形式下, Identifier 仅用一个字节表示. bit8 和 bit7 用来表示 class, bit6 为 0, 用来表示是基本类型. bit5-1表示 tag number.

    high tag number form: 这种形式下, Identifier 会占用一到多个字节. 第一个字节任然是 low tag number form 形式. 但是 bit5-1 应该全为 1. 其余比特用来表示 tag number, 高字节在前.

    Class Bit 8 Bit 7
    Universal 0 0
    Application 0 1
    Context-specific 1 0
    Private 1 1

    对于 Length, 也有两种形式:

    Short form: 这种形式下, Length 占用一个字节. bit 8 为 0, 其余七字节用于编码长度, 因此这种形式用于表示长度 0-127.

    Long form: 这种形式下, Length 占用的字节长度为 2-127 个字节. 第一个字节的 bit8 为 1, 其余字节用于表示紧接着有多少个字节用来编码长度. 高字节在前.

  2. Contructed, definite-length encoding 这种编码方法用于编码复杂类型和简单类型中的字符串类型且编码长度已知.编码形式与 Primitive, definte-length encoding 类似. 除了 Identifier 字段的第一个字节的 bit6 为 1, 用来表示是复杂类型.

  3. Constructed, indefinite-length encoding 这种编码方法用于编码复杂类型和简单类型中的字符串类型且编码长度未知.编码形式如下:

    在这里插入图片描述

    这里 Length 字段的值为 0x80, End-of-contents 字段的值为两个字节的 0. 表示该编码方式为不定长编码.其余字段的编码与 Contructed, definite-length encoding 类似.


Distinguished Encoding Rules


Distinguished Encoding Rules 简称为 DER, 是 DER 编码的一个子集. 区别在于 BER 编码结果不唯一, 而 DER 编码结果唯一.

DER 编码在 BER 编码的规则之上添加了如下限制:

  • 当编码长度在 0-127 之间时, 必须使用 short form 形式编码 Length 字段
  • 当编码长度大于 128 时, 必须使用 long form 形式且应使用最少的字节对长度进行编码,也就是说如果长度为192, 那么 Length 字段只能占两个字节. 第一个字节为0x81, 表示 Length 字段使用 Long form, 且紧接着只有一个字节用于编码长度信息. 第二个字节为 0xc0, 用来表示长度为 192
  • 对于简单字符串类型, 必须使用 primitive, definite-length 进行编码
  • 对于复杂类型, 必须使用 constructed definite-length 进行编码

编码示例


BIT STRING

00000110011011100101110111000000 进行编码,

对于 BIT STRING 类型, Identifier 字段应该为 03(这里我们不考虑 class, 仅仅考虑 tag number). 对于 Length 字段, 因为每个1或者0代表一个bit, 因此该值需要 4 字节进行编码, 因此 Length 字段应该为 04.

编码结果为:

BER short form encoding/DER encoding: 03 04 06 6e 5d c0
BER long form encoding: 03 81 04 06 6e 5d c0

IA5String

test1@rsa.com 进行编码, 编码结果为:

BER short form encoding/DER encoding: 16 0d 74 65 73 74 31 40 72 73 61 2e 63 6f 6d
BER long form encoding: 16 81 0d 74 65 73 74 31 40 72 73 61 2e 63 6f 6d

假设我们想将 test1@rsa.com 编码为一个 IA5String 的集合, 该集合中由三个 IA5String 构成: test1, @, rsa.com.

这里我们要编码一个复杂类型, 那么 Identifier 的 bit6 应该为 1, 代表复杂类型. 而 IA5String 的 tag number 为 0x16, 因此 Identifier 的值应该为 0x36. 因此编码结果如下:

36 13
16 05 74 65 73 74 31
16 01 40
16 07 72 73 61 2e 63 6f 6d

INTEGER

对 0 进行编码, 编码结果如下: 02 01 00

对 127 进行编码, 编码结果如下: e>02 01 7F

对 128 进行编码, 编码结果如下: 02 02 00 80

NULL

编码结果为: 05 0005 81 00

OCTET STRING

对 01 23 45 67 89 ab cd ef 进行编码, 编码结果如下: 04 08 01 23 45 67 89 ab cd ef

UTCTime

将要进行 ANS1 编码的 UTCTime 应该是以下形式之一:

YYMMDDhhmmZ
YYMMDDhhmm+hh'mm'
YYMMDDhhmm-hh'mm'
YYMMDDhhmmssZ
YYMMDDhhmmss+hh'mm'
YYMMDDhhmmss-hh'mm'

where:

YY is the least significant two digits of the year
MM is the month (01 to 12)
DD is the day (01 to 31)
hh is the hour (00 to 23)
mm are the minutes (00 to 59)
ss are the seconds (00 to 59)
Z indicates that local time is GMT, + indicates that local time is later than GMT, and - indicates that local time is earlier than GMT
hh' is the absolute value of the offset from GMT in hours
mm' is the absolute value of the offset from GMT in minutes

对于时间 4:45:40 p.m. Pacific Daylight Time on May 6, 1991 进行编码, 它的所有表示形式中存在以下两种:

"910506164540-0700"
"910506234540Z"

对他们进行编码的结果为:

17 11 39 31 30 35 30 36 31 36 34 35 34 30 2D 30 37 30 30
17 0d 39 31 30 35 30 36 32 33 34 35 34 30 5a

OBJECT IDENTIFIER

一个 Object identifier 的含义是由 registration authorities 赋予的, 这里我们不多说. 列举一些 Oid 作为示例.

Object identifier value Meaning
{ 1 2 } ISOmember bodies
{ 1 2 840 } US (ANSI)
{ 1 2 840 113549 } RSA Data Security, Inc.
{ 1 2 840 113549 1 } RSA Data Security, Inc. PKCS
{ 2 5 } directory services (X.500)
{ 2 5 8 } directory services-algorithm

如何编码呢 ?

编码后的第一个字节的值为 40 * value1 + value2. (value1 要求是 0, 1 或者 2. value2 要求是 0-39.)

接下来的 value3, value4, …, valuen 以 128 为 base 进行编码, 高字节在前. 每个数字编码后除了最后一个字节外其他字节的 bit8 均为 1.

举个例子, 对 { 1 2 840 113549 } 进行编码:

首先, 40 * 1 + 2 = 42. 因此编码后第一个自己应该为 0x2a. 840 = 6 * 128 + 72. 因此 第二字节应该编码的值是 6 且 bit8 为 1, 因此第二位 0x86. 72 编码为第三字节, 为 0x48. 113549 = 6 * 1282 + 0x77 * 128 + 0x0d. 因此编码之后应该为 0x86, 0xF7, 0x0d. 因此,最终编码结果为 06 06 2a 86 48 86 f7 0d.


编码 Name (X.501 type)


这一节, 我们试着来对 Name 类型进行 ASN.1 编码.

Name 类型的 ASN.1 描述如下:

Name ::= CHOICE {
  RDNSequence }

RDNSequence ::= SEQUENCE OF RelativeDistinguishedName

RelativeDistinguishedName ::=
  SET OF AttributeValueAssertion

AttributeValueAssertion ::= SEQUENCE {
   AttributeType,
   AttributeValue }

AttributeType ::= OBJECT IDENTIFIER

AttributeValue ::= ANY

X.502 中定义 AttrubteType 可以有以下值:

attributeType OBJECT IDENTIFIER ::=
  { joint-iso-ccitt(2) ds(5) 4 }

countryName OBJECT IDENTIFIER ::= { attributeType 6 }

organizationName OBJECT IDENTIFIER ::=
  { attributeType 10 }

commonUnitName OBJECT IDENTIFIER ::=
  { attributeType 3 }

下面我们来一步一步的编码!

  1. AttributeType

    上述 countryName, organizationName, commonName 的值均为 OCTET STRING. 因此他们的 DER 编码方法应该为 primitive, definite-length. 对于 OBJECT IDENTIFIER 类型, Identifier 字段应该为 06. bit8 和 bit7 为 0, 代表 Universal class. 他们的 Oid 分别为 { 2 5 4 6 }, { 2 5 4 10}, { 2 5 4 3}. 因此编码分别为:

    06 03 55 04 06   // countryName
    06 03 55 04 0a   // organizationName
    06 03 55 04 03   // commonName
    
  2. AttributeValue

    假设上述 countryName, organizationName, commonName 属性的值类型均为 PrintableString, 且值分别为 “US”, “Example Organization”, “Test User 1”. 编码结果分别为:

    13 02 55 53  // "US"
    13 14 45 78 61 6d 70 6c 65 20 4f 72 67 61 6e 69 7a 61 74 69 6f 6e // "Example Organization"
    13 0b 54 65 73 74 20 55 73 65 72 20 31 // "Test User 1"
    
  3. AttributeValueAssertion

    AttributeValueAssertion 是 AttributeType 和 AttributeValue 的有序集合. 编码结果如下:

    30 09           // countryName = "US"
    06 03 55 04 06
    13 02 55 53 
    30 1b           // organizationName = "Example Organization"
    06 03 55 04 0a
    13 14 45 78 61 6d 70 6c 65 20 4f 72 67 61 6e 69 7a 61 74 69 6f 6e
    30 12          // commonName = "Test User 1"
    06 03 55 04 0b
    13 0b 54 65 73 74 20 55 73 65 72 20 31
    
  4. RelativeDistinguishedName

    RelativeDistinguishedName 是 AttributeValueAssertion 的无序集合. 编码结果如下, 为了直观, 我们省略了部分编码结果, 该结果均为上述步骤编码所得.

    31 0b
    30 09 ... 55 53
    
    31 1d
    30 1b ... 6f 6e
    
    31 14
    30 12 ... 20 31
    
  5. RDNSequence

    RDNSequence 是 RelativeDistinguishedName 的有序集合. 编码结果如下:

    30 42
    31 0b ... 55 53
    31 1d ... 6f 6e
    31 14 ... 20 31
    
  6. Name

    Name 的值是一个 CHOICE 类型. 编码结果如下:

    
    30 42
        31 0b
            30 09                                   
                06 03 55 04 06 // attributeType = countryName           
                13 02 55 53 // attributeValue = "US"
    
        31 1d
            30 1b
                06 03 55 04 0a // attributeType = organizationName                 
                13 14 45 78 61 6d 70 6c 65 20 4f 72 67 67 61 6e 69 7a 61 74 69 6f 6e  // attributeValue = "Example Organization"
    
        31 14
            30 12
                06 03 55 04 03 // attributeType = commonName                   
                13 0b 54 65 73 74 20 55 73 65 72 20 31 // attributeValue = "Test User 1"
    
    

Ending.

最后分享一个好用的在线解析 ASN.1 的工具: https://lapo.it/asn1js/.


Tags