1. 简介
ASN.1(Abstract Syntax Notation One)是一个标准的接口描述语言(Interface description language/IDL),IDL是可以在跨平台中实现序列化和反序列化的数据结构。
ASN.1只定义了抽象语法,并没有限定编码规则。当前较为流行的编码规则有:Basic Encoding Rules(BER)、Canonical Encoding Rules(CER)、Distinguished Encoding Rules(DER)。本文是基于ITU-T X.690规范初步阅读形成的一个笔记。
1.1 BER、CER、DER差异
BER是ITU-T X.690给出的传输ASN.1数据结构的一套基础编码规则,因此它的规则较为宽松和灵活,在规则范围内,发送放可以自由选择。例如一个boolean类型的true值,只要该字节非0x00即可,因此发送放可以选择0x01表示true,也可以选择0x02表示true。
DER和CER在BER基础上进一步添加限制,形成一套比BER严格的编码规则,因此DER和CER是BER的一个子集。
BER/CER/DER编码都是典型的TLV格式:Type-Length-Value
基础的TLV结构包含如下三部分:
- Type:Value标识符,标识Value类型
- Length:Value长度
- Value:数据内容
TLV允许多层嵌套,如图所示,1级Value中,嵌套1个2级TLV,2级TLV中又嵌套1个TLV队列(包含2个TLV)
DER和CER一个显著的差异在于:DER的length长度必须是明确的,而CER可以通过end-of-contents octets来表明Value结束(end-of-contents octets详见章节2.1和2.1.4)。
2.BER
2.1 基础规则
BER编码也遵循TLV结构,如下:
a. identifier octets
b.length octets
c.contents octets
d.end-of-contents octets
end-of-contents octets只在length octets中要求展现时,才会展现。默认不进行展现。
2.1.1 identifier octets
identifier octets包含三个部分:Class、P/C、Tag number。针对不同的Tag number又分为2种情况:
2.1.1 .1 Tag number小于31(十进制)
当ag number小于31时,identifier octets长度为1个字节,格式如下:
- Class:2个bit。包含4个值
a. 00:Universal。最常用的类型。在ITU-T X.680里定义的tag number均属于此类型。详见Tag number处表格。
b. 01:Application。
c. 10:Context-specific。
d. 11:Private。 - P/C:1个bit。标记后续contents octets是一个基础值还是一个嵌套的TLV值。
0:Primitive。后续contents octets为基础值
1:Constructed。后续contents octets为嵌套的TLV。 - Tag number:5个bit。最大值为0x1F,即十进制的31。
Type中字段规范表如下:
Name | Class | P/C | Decimal Tag number | Hexadecimal Tag number |
---|---|---|---|---|
End-of-Content(EOC) | Universal | Primitive | 0 | 0 |
BOOLEAN | Universal | Primitive | 1 | 1 |
INTEGER | Universal | Primitive | 2 | 2 |
BIT STRING | Universal | Both | 3 | 3 |
OCTET STRING | Universal | Both | 4 | 4 |
NULL | Universal | Primitive | 5 | 5 |
OBJECT IDENTIFIER | Universal | Primitive | 6 | 6 |
Object Descriptor | Universal | Both | 7 | 7 |
EXTERNAL | Universal | Constructed | 8 | 8 |
REAL (float) | Universal | Primitive | 9 | 9 |
ENUMERATED | Universal | Primitive | 10 | A |
EMBEDDED PDV | Universal | Constructed | 11 | B |
UTF8String | Universal | Both | 12 | C |
RELATIVE-OID | Universal | Primitive | 13 | D |
TIME | Universal | Primitive | 14 | E |
Reserved | Universal | / | 15 | F |
SEQUENCE and SEQUENCE OF | Universal | Constructed | 16 | 10 |
SET and SET OF | Universal | Constructed | 17 | 11 |
NumericString | Universal | Both | 18 | 12 |
PrintableString | Universal | Both | 19 | 13 |
T61String | Universal | Both | 20 | 14 |
VideotexString | Universal | Both | 21 | 15 |
IA5String | Universal | Both | 22 | 16 |
UTCTime | Universal | Both | 23 | 17 |
GeneralizedTime | Universal | Both | 24 | 18 |
GraphicString | Universal | Both | 25 | 19 |
VisibleString | Universal | Both | 26 | 1A |
GeneralString | Universal | Both | 27 | 1B |
UniversalString | Universal | Both | 28 | 1C |
CHARACTER STRING | Universal | Constructed | 29 | 1D |
BMPString | Universal | Both | 30 | 1E |
DATE | Universal | Primitive | 31 | 1F |
TIME-OF-DAY | Universal | Primitive | 32 | 20 |
DATE-TIME | Universal | Primitive | 33 | 21 |
DURATION | Universal | Primitive | 34 | 22 |
OID-IRI | Universal | Primitive | 35 | 23 |
RELATIVE-OID-IRI | Universal | Primitive | 36 | 24 |
2.1.1.2 Tag number大于31
当ag number大于等于31时,因1个字节的identifier octets只有5bit的tag number,无法表示31以上的值,此时需要identifier octets长度超过1个字节,identifier octets格式如下:
- Class、P/C的值和Tag number小于31时无差异
- 第一个字节的tag number字段(bit 1到bit 5)全部为二进制的1。后续字节的最高位bit8为标记位,若该位为0,则表明该字节为identifier octets最后1个字节,否则该位为1。Tag number由所有的Subsequent octet中bit7到bit1按照大端模式拼接而成。
2.1.2 length octets
length octets如下图所示
共存在4中场景:
1.Contents octets长度小于等于127字节:此时length octets长度为1个字节,bit 8为0,bit7~bit1存储Contents octets长度值。
2.length octets无需指明Contents octets长度:此时length octets长度为1个字节,bit 8为1,bit7~bit1全部为0。Contents octets结束后必须紧跟end-of-contents octets,用于表明Contents octets结束。
3.Contents octets长度大于127字节:此时length octets长度超过1个字节。bit 8为0,bit7~bit1表明后续还有几个字节。后续的字节值代表Contents octets的长度。例如Contents octets长度为290个字节,则length octets第1个字节值为0x82(bit8~bit1分别为10000010),第2个字节为0x01,第3个字节为0x22,最终的Contents octets长度为0x0122,即290个字节。
4.保留:此时length octets长度为1个字节,bit 8为1,bit7~bit1全部为1
总体上来说,发送者可以选择指定长度或不指定长度两种方式。是否指定长度,需遵守如下规则:
1.如果P/C是primitive,则必须在length octets中指定长度
2.若P/C是constructed且值是已知的,则两种方式都可以选择
3.若P/C是constructed且值不可知,则使用不指定长度的方式
2.1.3 contents octets
对数据值进行编码后保存在contents octets中,contents octets可以有0个、1个或多个字节组成。contents octets需要和tag number对应的Type类型匹配。
2.1.4 end-of-contents octets
当length octets为10000000,即0x80时,表明未指定contents octets长度。此时,当contents octets结束时,必须紧跟end-of-contents octets。end-of-contents octets为2个字节的0x00。
2.2 数据类型介绍
针对Contents octets的不同类型数据,本章节将介绍部分常见数据值。数据类型总览表如下:
Name | Class | P/C | Decimal Tag number | Hexadecimal Tag number |
---|---|---|---|---|
End-of-Content(EOC) | Universal | Primitive | 0 | 0 |
BOOLEAN | Universal | Primitive | 1 | 1 |
INTEGER | Universal | Primitive | 2 | 2 |
BIT STRING | Universal | Both | 3 | 3 |
OCTET STRING | Universal | Both | 4 | 4 |
NULL | Universal | Primitive | 5 | 5 |
OBJECT IDENTIFIER | Universal | Primitive | 6 | 6 |
Object Descriptor | Universal | Both | 7 | 7 |
EXTERNAL | Universal | Constructed | 8 | 8 |
REAL (float) | Universal | Primitive | 9 | 9 |
ENUMERATED | Universal | Primitive | 10 | A |
EMBEDDED PDV | Universal | Constructed | 11 | B |
UTF8String | Universal | Both | 12 | C |
RELATIVE-OID | Universal | Primitive | 13 | D |
TIME | Universal | Primitive | 14 | E |
Reserved | Universal | / | 15 | F |
SEQUENCE and SEQUENCE OF | Universal | Constructed | 16 | 10 |
SET and SET OF | Universal | Constructed | 17 | 11 |
NumericString | Universal | Both | 18 | 12 |
PrintableString | Universal | Both | 19 | 13 |
T61String | Universal | Both | 20 | 14 |
VideotexString | Universal | Both | 21 | 15 |
IA5String | Universal | Both | 22 | 16 |
UTCTime | Universal | Both | 23 | 17 |
GeneralizedTime | Universal | Both | 24 | 18 |
GraphicString | Universal | Both | 25 | 19 |
VisibleString | Universal | Both | 26 | 1A |
GeneralString | Universal | Both | 27 | 1B |
UniversalString | Universal | Both | 28 | 1C |
CHARACTER STRING | Universal | Constructed | 29 | 1D |
BMPString | Universal | Both | 30 | 1E |
DATE | Universal | Primitive | 31 | 1F |
TIME-OF-DAY | Universal | Primitive | 32 | 20 |
DATE-TIME | Universal | Primitive | 33 | 21 |
DURATION | Universal | Primitive | 34 | 22 |
OID-IRI | Universal | Primitive | 35 | 23 |
RELATIVE-OID-IRI | Universal | Primitive | 36 | 24 |
若需了解详细规则,请参考ITU-T X.680相关标准。
2.2.1 Boolean
Boolean的Contents octets只占用1个字节。当boolean值为FALSE,contents octets值为0x00。当boolean值为TRUE,contents octets值为非0x00,即范围在0x01到0xFF均合法,允许发送发自由定义。
示例
值为True的TLV结构
T | L | V |
---|---|---|
0x01 | 0x01 | 0xFF |
2.2.2 INTEGER
integer类型的contents octets可以包含1个到多个字节。若contents octets包含多个字节时,第一个字节和第二个字节的bit8,不能全为1也不能全为0。contents octets应使用数值的补码。
示例
值为65537的TLV结构
T | L | V |
---|---|---|
0x02 | 0x03 | 0x01 0x00 0x01 |
2.2.3 NULL
null类型的不包含contents octets,length octets为0
示例
T | L |
---|---|
0x05 | 0x00 |
2.2.4 UTF8String
UTF8编码的字符串
示例
字符串“tom”
T | L | V |
---|---|---|
0x0C | 0x03 | 0x74 0x6F 0x6D |
2.2.5 SEQUENCE
标识contents octets是一个队列
示例
SEQUENCE {name UTF8String, ok BOOLEAN}对应的值为{name “tom”,ok TRUE}
T | L | V |
---|---|---|
0x30 | 0x08 | 0x0C 0x03 0x74 0x6F 0x6D 0x01 0x01 0xFF |
3.CER和DER
CER和DER在BER基础上,进一步添加限制规则。
3.1 CER特有限制规则
1.如果tag number是constructed的,则Length octets应不指定长度(值为0x80)
2.bitstring/octetstring和restricted character string的contents cotets长度不超过1000个字节时,使用primitive编码。若超过10000个字节,应使用Constructed编码,每个string段长度为1000个字节且使用primitive,最后一个string段至少1个字节但不能超过1000个字节。(按标准翻译,没太理解)
3.set类型排序规则。
3.2 DER特有限制规则
1.length octets必须指明长度,且使用最小的字节数进行编码。
2.对于bitstring、octetstring、restricted character string类型,P/C不使用constructed。
3.set类型排序规则。
3.3 CER和DER公用规则
1.Boolean类型的TRUE值必须为0xFF
2.bitstring的最后一个字节里,未使用的bit需置0
3.当满足 ITU-T X.680 | ISO/IEC 8824-1, 22.7中内容时,在编码前,需移出尾部的所有0
等等(具体请查询X.690规范,此处未做深入研究)
4.总结
因公私钥、证书等均采用ASN.1进行编码,为更深入了解公私钥和证书信息,首先需要了解ASN.1和DER等规范。通过阅读ITU-T X.690规范,可以有效的初步掌握此类基础信息。若需深入了解不同数据的编码细节,需结合X.680,X.690等规范,深入阅读。
X.690规范的核心是TLV的数据结构。在TLV中,基于V的不同类型,产生了不同的T,L则用于表示V的长度,总体上形成一套完善的编码规范。