【网络】ASN.1语法表示

介绍

ASN.1语法可用于表示与编程语言无关的数据结构。比如,RFC X509就使用了ASN.1语法表示证书的结构。

本博客主要参考:http://luca.ntop.org/Teaching/Appunti/asn1.html

在线解析工具:http://lapo.it/asn1js/

基于本博客编写的ASN.1解析库: https://github/xhd2015/net-protocol-tools (见包名com.fulton_shaw.net.ans1)

基础

7位分组编码

当一个数据需要使用多个字节来表示时,我们按照下面的原则来选择编码方式:
1.能够与单字节编码区分,在连续的字节流中能够识别边界;
2.尽可能少地占用空间

多字节分散编码:
1.将源数据按7位分组,每组填充到一个字节中的低7位;
2.除了最低位字节的高位是0,其他字节的高位都是1;这样能够区分边界;因为高位是1的字节一定不是第一个字节;
3.如果值小于128,即可用7位表示,则只使用一个字节即可。

英文描述:

the number,base 128, most significant digit first, with as few digits as possible, and with the bit 8 of each octet except the last set to “1.”

下面语法中的tag编码,Object Identifier编码均使用这种方式。
对应工具包中的方法:com.fulton_shaw.net.tls_https.EncodingUtils.encode7BitGroupToByteArray(byte[])
com.fulton_shaw.net.tls_https.EncodingUtils.decode7BitGroup(byte[])

示例:
01 bb 8d 编码为 86 f7 0d
80 编码为 8100
7f 编码为 7f

这种编码的好处是能够在编码序列中区分边界。

基本语法

类型

在ASN.1中,类型是指一组值,包括有限的值和无限的值;值是指某个类型的一个元素。
四种类型:1.简单类型,原子类型 2.结构类型,由其他类型组成 3.CHOICE类型,类似于联合 4.ANY,任意类型。
可以使用:==赋值符号赋予类型或者值一个名称,这个名称可用于定义其他类型。

注意,标识符可以由字母,数字,空格,短线组成。

tag

除了CHOICE和ANY类型,其他类型都有一个tag,包括一个class和一个非负的tag编号。ANS.1的两个类型的等价性完全由tag编号决定,而不受名称的影响。

有四种类型的tag:1.Universal 全局的 2.Application 应用的 3.Private 私有的,仅企业使用 4.Context-Specific 上下文相关

类别classbit8 bit7
全局00
应用01
上下文相关10
私有11

全局类型的tag映射表

类型tag编号(16进制)
INTEGER02
BIT STRING03
OCTET STRING04
NULL05
OBJECT IDENTIFIER06
SEQUENCE and SEQUENCE OF10
SET and SET OF11
PrintableString13
T61String14
IA5String16
UTCTime17

简单类型

BIT STRING 任意长字符串
IA5String 即任意长ASCII字符串
INTEGER 任意整数
OBJECT IDENTIFIER 对象标识,一组数字,用来表示算法或属性
OCET STRING 字节数组
PrintableString 可打印的字符串
UTCTime UTC时间

结构类型

SEQUENCE, SEQUENCE OF 分别表示0到多个和1到多个类型的有序集合
SET, SET OF 表示无序集合

显式tag和隐式tag

显式tag:[class number] EXPLICIT 编码更长,能够区分CHOICE和ANY
隐式tag: [class number] IMPLICIT 编码更短,但是不能区分CHOICE和ANY

编码

基本编码规则(BER),用于将ASN.1类型表示为字节数组。
有三种编码方法:1.原始类型,定长编码 2.构造类型,定长编码 3.构造类型,非定长编码。
简单的数字类型使用第1种编码,结构使用第2或3种编码,字符串可能使用任何一种。

编码规则将结果分为3或者4个部分:
1.标识,用多个字节标识classtag,同时指定编码方法是原始类型还是构造类型
2.长度,对于定长编码,这里表示长度;非定长编码,第一个字节标识额外的用于表示长度的字节数,后面的字节标识真实的长度。这里可用于表示定长
3.内容,对于原始类型和定长编码,这里表示的就是内容本身;对于构造类型,这里表示多个编码结果的组合
4.尾部,对于非定长编码,这里表示内容结束;其他编码方法没有这个部分。

原始类型的定长编码

标识字节

对于小于等于30的tag,可以使用8位来表示class和tag编号,如下分配

bit8 + bit7: 00 = 全局 01=应用 10=上下文相关 11=私有
bit6 = 0
bit5~bit1 表示tag编号

对于大于30的tag,多个字节标识,第一个字节和上面一样,但bit5~bit1全都是1,后序的字节使用上面定义的7位分组编码

长度

短形式,单字节,bit8=0,bit7~bit1表示长度
长形式,多字节, 第一个字节的bit8=1, bit7~bit1表示长度域的长度

定长构造类型编码方法

标识字节,同上面,但bit6=1,表示构造类型
长度同上
内容,拼接

非定长构造类型编码方法

标识字节,同上
长度:单字节 0x80
内容:同上
结尾:两个字节, 0x00,0x00,类型的class和tag都是0。

编码方法选择

DER,BER的子集,用于限定编码选择。
1.如果长度在0~127之间,必须采用长度的短形式
2.如果长度在128或者之上,必须采用长度的长形式
3.简单的字符串类型,由字符串tag推导的类型,原始类型必须使用原始类型定长编码
4.构造类型,由构造类型推导的隐式tag类型,显式tag类型,必须使用构造类型的定长编码

隐式tag类型

语法: [[class] tag_number ] IMPILICT Type
其中class可以省略,默认为context-specific, tag_number是在class中的类型。

显式tag类型

显式tag类型需要在结构体之前加上额外的标签信息。

注:如果省略了IMPLICIT或EXPLICIT关键字,则使用模块定义的默认策略。对于X509v3而言,默认策略是IMPLICIT

Object Identifier

示例参考:https://docs.microsoft.com/zh-cn/windows/win32/seccertenroll/about-object-identifier?redirectedfrom=MSDN

用于定义一组用数字标识的标识符。
首先,会定义一个前缀:

-- Arc for standard naming attributes

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

id-at是前缀,值顺序是2,5,4, 定义中的标识符可以忽略。
然后,定义具体的类型值:

id-at-commonName        AttributeType ::= { id-at 3 }

则意味着 id-at-commonName的值是{2,5,4,3},可以表示为2.5.4.3.

编码规则

该类型的tag是0x06,长度由实际编码后的长度决定。
编码规则如下:

前两个数(至少包含两个数)使用 40*value1 + value2的格式编码,因为value2总是小于40,编码后的结果形成第一个字节;

后面的字节,
1.如果小于128,即小于8位,则按字节填入即可比如2.5.4.3可以编码为55 04 03, 其中0x55/40=2, 0x55%40=5;

2.大于等于8位,需要拆分成多个字节处理,除了第1个字节的高位是0,其他字节的高位都是1;编码的结果是使得结果的每个字节的低7位拼接起来,能够形成源数据。
比如113549,其二进制表示是0b1_1011_1011_1000_1101
a.第一个字节应当表示低7位,且高位为0,结果为: 0b0000_11010x0d;
b.第二个字节应当表示第2批7位,且高位为1,结果为: 0b1111_0111,即0xf7;
c.第三个字节应当表示第3批7位,且高位为1,结果为: 0b1000_0110,即0x86
所以,最终的编码结果是 86 f7 0d.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值