简单编码规则
基本编码规则(Basic Encoding Rule)把 ASN.1 表示的抽象类型值编码为字节串,这种字节串的结构为类型—长度—值,简称 TLV(Type-Length-Value)。
其中 Type 和 Length 都是1个字节,而且值部分还可以递归地再编码为 TLV 结构,这样就具有了表达复杂结构的能力。
编码的第一个字节表示 ASN.1 类型或用户定义的类型,前2位用于区分 4 种标签,第 3 位用于区分简单类型和构造类型,其余 5 位表示标签的值。
结合上述的说明,总结出 Type 字段的编码格式如下:
留个悬念:5 位 2 进制的表示范围是 0~31,为什么对标签号的扩充以 30 为界限?
简单编码例题
部分常用标签
例题一
布尔类型 FALSE、TRUE 的编码,都用一个字节表示,FALSE 是 00,TRUE 是 FF。Bool 类型是通用标签 UNIVERSAL,属于简单类型,标签号为 1 号。根据上述的表格就可以写出 Type 字段的二进制表示为 00000001,十六进制表示为 01。由于 FALSE、TRUE 都用一个字节表示,因此 L 字段值为 00000001,十六进制表示为 01。
Type | Length | Value |
---|---|---|
00 0 00001 | 00000001 | 00000000 |
01 | 01 | 00 |
Type | Length | Value |
---|---|---|
00 0 00001 | 00000001 | 11111111 |
01 | 01 | FF |
例题二
十进制数 256 的编码,把这个数用二进制表示就是 00000001 00000000,十六进制为 01 00 需要 2 字节。int 类型是通用标签 UNIVERSAL,属于简单类型,标签号为 2 号,根据上述的表格就可以写出 Type 字段:00000010。由于这个数用二进制表示需要 2 字节,所以 L 字段值为 00000010,十六进制表示为 02。
Type | Length | Value |
---|---|---|
00 0 00010 | 00000010 | 00000001 00000000 |
02 | 02 | 01 00 |
例题三
比特串 10101 的编码,由于传输时以字节为最小单位,不足部分补 0,因此需要有额外的字节来表示有多少比特是补足的。比特串第三个字节 03 表示值部分的最后 3 个比特未用,第四个字节是值部分,A8H 即 10101000。由于需要 2 个字节表示,所以 L 字段值为 02。
bit str 类型是通用标签 UNIVERSAL,属于简单类型,标签号为 3 号,根据上述的表格就可以写出 Type 字段:00000011。
Type | Length | Value |
---|---|---|
00 0 00011 | 00000010 | 00000011 10101000 |
03 | 02 | 01 A8 |
例题四
字节串 ACE 的编码,由于字节串总是占用整数个字节,因而不必说明未占用的比特数。没有说明值的位都认为是 0,故最后一个字节写为 E0,可见字节串类型也遵循靠左存放的原则。
octet str 类型是通用标签 UNIVERSAL,属于简单类型,标签号为 4 号,根据上述的表格就可以写出 Type 字段:00000100。
Type | Length | Value |
---|---|---|
00 0 00100 | 00000010 | 10101100 11100000 |
04 | 02 | AC E0 |
例题五
NULL 的编码,NULL 类型只有一个值 NULL,其标签是 UNIVERSAL 5。由于这个类型是空类型,无需存储或传送它的值,第二个字节 00 表示值长度为 0。
Type | Length | Value |
---|---|---|
00 0 00101 | 00000000 | |
05 | 00 |
例题六
序列类型 SEQUENCE{madeofwood BOOLEAN, length INTEGER} 的值 {madeofwood TRUE,length 62}的编码。SEQUENCE 类型是通用标签 UNIVERSAL,属于构造类型,标签号为 16 号,根据上述的表格就可以写出 Type 字段:00110000。
此处用到的是 TLV 结构的嵌套方式,表示出了 SEQUENCE 类型的编码。
例题七
集合类型 SET{breadth INTEGER, bent BOOLEAN} 的值 {breadth 7, bent FALSE} 的编码。SET 类型是通用标签 UNIVERSAL,属于构造类型,标签号为 17 号,根据上述的表格就可以写出 Type 字段:00110001。由于集合类型的元素是无序的,所以有 2 种编码的方式。
例题八
在安全协议设计中定义了一个口令字类型,并赋予应用标签 27:Password::=[APPLICATION 27] OCTET STRING。Password 是 APPLICATION 类型标签,是构造类型,标签号为 27 号,根据上述的表格就可以写出 Type 字段:01111011。
对于这个类型的一个值 “Sesame”,可得到如下编码:
字段扩充
对标签值扩充
扩充情况
例如写出标签[APPLICATION 35]的二进制表示,写出来是 01110001 1 需要 9 位二进制才能编码,但是 Type 是 1 个字节显然不够,这时就需要对标签值进行扩充。
扩充方法
当标签值大于 30 时,需要进行扩充。方法是当标签值大于等于 31 时这 5 位置全 1 作为转义符,实际的标签值编码表示在后续字节中。
后续字节的左边第一位表示是否为最后一个扩充字节,只有最后一个扩充字节的左边第一位置 0,其余扩充字节的左边第一位置 1。这样每个扩充字节只用了 7 位表示标签值的编码。
扩充样例
例如标签值 101 10010101 111001,由于表示标签号的二进制位数超过 5 位需要字段扩充。因此将标签值按照 7 位一组分开,其中最后一个字节的第一位置为 0,其他 2 个字节的第一位置为 1,用 0 填充空位后可编码为:
×××11111 10000101 11001010 01111001
对 Length 字段扩充
扩充情况
Length 字段用于表示 Values 字段有多少字节,Length 为 1 个字节最多表示 Values 最多有 2^8 个字节。 当值部分长度超级长,超过了 2^8 个字符,Length 字段(8 位二进制)还能够表示出 Value的长度吗?显然不行,所以需要进行扩充。
扩充方法
对长度字节的扩充方法是当小于 127 的数用长度字节的右边 7 位表示,最左边的一位置 0。
大于等于 127 的数用后续若干字节表示,原来的长度字节第一位置 1,其余 7 位指明后续用于表示长度的字节数。值得注意的是长度字节可表示的最大值为 126 而不是 127,这个值是为以后扩充保留的。
扩充样例
例如 L 字段的值为 321 时,由于 321 的二进制表示为 00000001 01000001 需要 2 字节,需要进行扩充。原来的长度字节第一位置 1,其余 7 位指明后续用于表示长度的字节数,此处扩充 2 个字节因此为 10000010,扩充后表示为:
10000010 00000001 01000001
参考资料
《计算机网络管理(第三版)》雷震甲 编著,西安电子科技大学出版社