简介
- ASN.1(Abstract Syntax Notation One),抽象语法标记。
- ASN.1是一种国际标准的正式语言,由国际标准化组织(ISO)和国际电信联盟(ITU-T)共同制定,用于定义数据结构的抽象语法。它的设计目标是为了提供一种独立于特定计算机硬件、操作系统或编程语言的方式,来描述数据的结构和编码规则,以便在网络上传输和处理数据。
- 那么为什么要熟悉ASN.1编码?因为在密码学中,像数字证书、公私钥、P7数字签名等信息都是ASN.1编码的数据。熟悉ASN.1编码才能更方便的去分析数据。
格式
- <新类型的名字> ::=<类型描述>
- <新类型的名字>: 一个以大写字母开头的标识符
- <类型描述>: 基于内建类型或在其它地方定义的类型
- 示例
-
SM2Signature ::=SEQUENCE{ R INTEGER, S INTEGER }
-
DER编码
-
ASN.1的编码格式有很多种: BER、CER、DER、XER,密码学上大部分使用DER编码。
-
DER编码遵循TLV格式,即Type,Length,Value
-
Type
通常为1个字节,创建类型如下:编码 类型 描述 类别 0x01 BOOLEAN 布尔类型,TRUE或FALSE 基本类型 0x02 INTEGER 用于表示整数值,可以是正数、负数或零 0x03 BIT_STRING 位字符串,每个位可以独立设置为0或1 0x04 OCTET_STRING 八进制字符串 0x05 NULL 表示一个特殊的空值 0x06 OID 用于唯一标识ASN.1定义的对象或类型 0x0c UTF8String Unicode字符的UTF-8编码字符串 字符串类型 0x30 SEQUENCE 包含一个或多个类型的有序字段系列 构造类型 0x31 SET 包含一个或多个类型的无序字段系列 -
Length
- 为定长及不定长类型,一般为1-3字节,定长分为短格式和长格式。这里只介绍定长格式。
- 如果value的值小于等于127个字节,那么Length占用一个字节,表示Value值的长度。
- 如果value的值大于等于128个字节,则第一个字节的最高位会被设置为1,其余的7位用于表示后续字节中用来表示实际长度的字节数。之后的字节则按顺序组成实际的长度值。
- 如图,Value长度为52个字节,Length占用1个字节,值为52,表示的就是Value的长度。
- Value的值为4934个字节时,Length占用了3个字节,第一个字节的最高位被置为1,其余7位表示Length还需要占用多少个字节,这里值为2,表示Length还需要占用2个字节,往后再取2个字节,值为4934个字节,表示Value的实际值为4934个字节。
-
Value: 存储实际的数据值,其格式和内容依据前面的Type和Length决定
类型详解
- ASN.1数据在线解析网站
- OID在线查询网站
- 数据类型中,INTEGER、BIT STRING和OID这三种类型编码比较复杂,也比较难理解,会重点进行介绍。
BOOLEAN
- 布尔值可以是 TRUE 或 FALSE
INTEGER
- 如果整数为正数,但高阶位设置为 1,则会将前导0x00添加到内容中,以指示数字不是负数。 例如,0x8F (10001111) 的高阶字节为 1。 因此,将前导零字节添加到内容,如下图所示。
- 解析一个SM2加密后的ASN.1编码的密文数据分析以下。
- 看下第一个整数,从第3个字节开始,02表示类型,20表示整数的值长度,为32个字节。从07开始到1D,就是实际的Value值。
- 再看第2个整数,02表示类型,21表示长度,为33个字节。然后是Value值,可以看到Value值的第一个字节是00,这是填充的数据,后面AB开始才是实际的Value值。为什么要填充00?因为整数值的最高位表示正负数,AB值的最高位为1,如果不填充00,就会把AB开始的Value值当作负数处理,因此最高位填充00,表示正数。
BIT STRING
- BIT STRING类型是用来表示任意长度的二进制位序列,这种类型可以包含0个或多个比特,并且可以明确指定哪些比特位是重要的或者说是意义明确的,而其余未标记的比特位通常是填充的或应被忽略的。
- 如图所示,BIT STRING类型的V部分的第一个字节为数据填充标志,表示实际数据需要填充多少位。如果实际数据长度的位数不是8的整数倍,需要填充成8的整数倍,具体填充多少位,就在V的第一个字节中表示。如果实际数据长度的位数是8的整数倍,不需要填充,则V的第一个字节数据填充标志就为0x00。数据填充标志后面部分才是实际的数据值。
- 如上图所示,T为0x03,表示类型为 BIT_STRING。L为0x03,表示数据长度占3个字节。V的第一个字节为0x04,表示实际的值有四个未使用的位,需要填充,可以看到V的第三个字节的后四位填充了xxxx,填充的值是什么无所谓,但是这四位的值是不使用的,一般都是填充0。
- 再分析以下一组Hex数据
-
03 81 81 00 47 eb 99 5a df 9e 70 0d fb a7 31 32 c1 5f 5c 24 c2 e0 bf c6 24 af 15 66 0e b8 6a 2e ab 2b c4 97 1f e3 cb dc 63 a5 25 ec c7 b4 28 61 66 36 a1 31 1b bf dd d0 fc bf 17 94 90 1d e5 5e c7 11 5e c9 55 9f eb a3 3e 14 c7 99 a6 cb ba a1 46 0f 39 d4 44 c4 c8 4b 76 0e 20 5d 6d a9 34 9e d4 d5 87 42 eb 24 26 51 14 90 b4 0f 06 5e 52 88 32 7a 95 20 a0 fd f7 e5 7d 60 dd 72 68 9b f5 7b 05 8f 6d 1e
-
- 第一个字节为0x03,表示类型为BIT_STRING。第二个字节为0x81,二进制为 10000001,可以看到最高位为1。回顾上文中Length的编码规则,如果Length的第一个字节最高位为1,则其他7位表示Length占用了几个字节,这里其他7位为1,表示Length占用了一个字节,往后再取一个字节,为0x81,这个表示的才是Value的实际长度,为129个字节。后面就是Value部分,第一个字节为0x00,表示数据部分不需要填充,0x00后面的内容就是实际的值。
- 我们通过ASN.1数据在线解析网站 分析下这部分数据。
- 可以看到,Length表示的长度为 129个字节,但实际数据长度是1024位,也就是128个字节。说明Value部分的第一个字节只表示填充信息,不是实际的数据信息。
- 那么有以下两组位串数据,我们尝试进行下ASN.1编码
-
{1,1,0,1,0,1,0,1} {1,0,1,1,0,1,1,1,0,1,0,0,1}
- 第一组数据进行编码,类型为 0x03,数据长度为1个字节,再加一个填充标识字节,Length为 0x02,数据长度为8位,不需要填充,则Value部分的第一个字节为0x00,第二个字节为实际的值 0xd5。则最终编码值为 030200d5。
- 第二组数据进行编码,类型为 0x03,数据长度为2个字节,再加一个填充标识字节,Length为0x03,数据长度为13位,必须为8的倍数,因此填充3个字节,则Value部分的第一个字节填充标志为0x03。这里可以直接填充0,那么实际的值为1011011101001000,即为0xb748。最终编码值为 030303b748。
- 分别解码看下
-
NULL
- 长度为 0x00,且不带值字节
OID
-
OID(OBJECT IDENTIFIER)对象标识符。
-
OID编码规则
- 第一个字节编码是固定的,为OID数据的第1个数据乘以40再加上第2个数据。
- 后面的其他数据,如果小于等于127,则直接进行编码。
- 如果数据大于127,需要用多个字节表示。先将这个数据拆分为 x * 128 + y的形式,比如401 = 3 * 128 + 17的形式,然后第一个字节就为x,第二个字节为y。x为0x03,二进制为 00000011,但是x不能编码出401,需要和y一起编码才能表示401,因此需要将x的最高位替换为1,表示后面还有字节来编码401,替换后为10000011,编码为0x83,y为17,二进制为 00010001,x和y可以成功编码出401,后面没有其他数据了,y就不需要替换最高位,直接编码为 0x11,则401的最终编码为 0x83,0x11。
-
SM3WITHSM2的OID为 1.2.156.10197.1.501,尝试进行下编码
-
为了方便,这里用 a.b.c.d.e.f 代表 1.2.156.10197.1.501
-
先编码1个字节。值为 1 * a + b = 1 * 40 + 2 = 42,编码为0x2A。
-
再编码第2个和第3个字节。c = 156 = 1 * 128 + 28,则第二个字节1,二进制为 00000001,最高位需要替换为1,表示后续还有其他字节,为 10000001,编码为0x81,第三个字节为28,二进制为00011100,后续没有其他字节,直接编码为0x1C。
-
接着编码第4个和第5个字节。d = 10197 = 79 * 128 + 85,则四个字节为79,二进制为01001111,后面还有其他字节,第一位替换为1,为11001111,编码为0xCF,第五个字节为85,二进制为01010101,后续没有其他字节,编码为0x55。
-
第6个字节编码。e = 1,直接编码为0x01
-
第7个和第8个字节编码。f = 501 = 3 * 128 + 117,第7个字节为3,二进制为 00000011,后面还有其他字节,最高位替换为1,为 10000011,编码为0x83,第8个字节为117,二进制为01110101,编码为0x75。
-
最终编码值为 2A 81 1C CF 55 01 83 75,加上T和L,则OID最终编码为 06 08 2A 81 1C CF 55 01 83 75
-
国密算法的OID含义
对象标识符OID 对象标识符定义 类别 1.2 国际标准化组织成员表示 通用对象标识符 1.2.156 中国 1.2.156.197 国家密码管理局 1.2.156.10197 国家密码行业标准化技术委员会 1.2.156.10197.1 密码算法 1.2.156.10197.1.100 分组密码算法 分组密码算法对象标识符 1.2.156.10197.1.102 SM1分组密码算法 1.2.156.10197.1.103 SSF33分组密码算法 1.2.156.10197.1.104 SM4分组密码算法 1.2.156.10197.1.200 序列密码算法 序列密码算法对象标识符 1.2.156.10197.1.201 祖冲之序列密码算法 1.2.156.10197.1.300 公钥密码算法 公钥密码算法对象标识符 1.2.156.10197.1.301 SM2椭圆曲线公钥密码算法 1.2.156.10197.1.301.1 SM2-1数字签名算法 1.2.156.10197.1.301.2 SM2-2密钥交换协议 1.2.156.10197.1.301.3 SM2-3公钥加密算法 1.2.156.10197.1.302 SM9标识密码算法 1.2.156.10197.1.302.1 SM9-1数字签名算法 1.2.156.10197.1.302.2 SM9-2密钥交换协议 1.2.156.10197.1.302.3 SM9-3密钥封装机制和公钥加密算法 1.2.156.10197.1.400 杂凑算法 杂凑算法对象标识符 1.2.156.10197.1.401 SM3密码杂凑算法 1.2.156.10197.1.401.1 SM3密码杂凑算法, 无密钥使用 1.2.156.10197.1.401.2 SM3密码杂凑算法, 有密钥使用 1.2.156.10197.1.500 组合运算机制 组合运算算法对象标识符 1.2.156.10197.1.501 基于SM2算法和SM3算法的签名 1.2.156.10197.1.504 基于RSA算法和SM3算法的签名 1.2.156.10197.4.3 CA代码 CA代码对象标识符 1.2.156.10197.6 标准体系 标准体系对象标识符 1.2.156.10197.6.1 基础类 1.2.156.10197.6.1.1 算法类 1.2.156.10197.6.1.1.1 《祖冲之序列密码算法》 1.2.156.10197.6.1.1.2 《SM4分组密码算法》 1.2.156.10197.6.1.1.3 《SM2椭圆曲线公钥密码算法》 1.2.156.10197.6.1.1.4 《SM3密码杂凑算法》 1.2.156.10197.6.1.2 标识类 1.2.156.10197.6.1.2.1 《密码应用标识规范》 1.2.156.10197.6.1.3 工作模式 1.2.156.10197.6.1.4 安全机制 1.2.156.10197.6.1.4.1 《SM2密码使用规范》 1.2.156.10197.6.1.4.2 《SM2加密签名消息语法规范》 1.2.156.10197.6.2 设备类 1.2.156.10197.6.3 服务类 1.2.156.10197.6.4 基础设施 1.2.156.10197.6.5 检测类 1.2.156.10197.6.5.1 《随机性检测规范》 1.2.156.10197.6.6 管理类
SEQUENCE
- 可以把 SEQUENCE 类型看成C语言中的结构体
- 解析一张x509格式的数字证书看下
- 可以看到 SEQUENCE 中包含了其他 SEQUENCE 类型或者基本类型。
- SEQUENCE 没有特殊的编码规则,就是按照标准 TLV格式进行编码。
SET
- SET 也可以看成是C语言中的结构体,与 SEQUENCE 不同的是,SET是有序的。
- 同样解析一个x509格式的数字证书
- 可以看到subject部分CN、ST、L、O、OU、CN的类型都为SET,说明必须按顺序进行排列。
参考
- ASN.1 类型的 DER 编码