文章目录
官方文档
https://www.itu.int/rec/T-REC-X/en
https://www.itu.int/rec/T-REC-X.690/en
本文中的一些格式说明
二进制数据
00101 2
十六进制数据
0xAFBD
或
AFBD 16
变长整数编码
在计算机中, 一个整数通常用1, 2, 4, 8个字节来表示. 而在通信中, 为了够表示任意长度的整数, 需要使用本节中的变长整数编码
基本原理:
1字节有8个bit, 使用它的低7位来存储数据, 最高位用于表示编码是否结束
最高位为1时, 表示编码没有结束, 它的下一个字节仍然是整数的一部分.
最高位为0时, 表示本字节已经是整数的最后一个字节, 编码结束.
例如, 十进制的201
, 它的二进制为11001001
2, 它的变长编码为
10000001 01001001
2
BER基本格式
有两种格式
格式1:
+---------------------------------------------------------------------+
| 识别码 | 长度 | contents ... |
+----------------------------------------------------------------------+
格式2
+------------------------------------------------------------------------+
| 识别码 | 0x80 | contents ... | 结束码 0x00 0x00 |
+------------------------------------------------------------------------+
识别码格式
8 7 6 5 4 3 2 1
+----------------------------------------------------------------------+----------
| class(2 bit) | p/c | tag number | tag number subsequence...
+----------------------------------------------------------------------+-----------
class, 高两位, 的值有
002 表示ASN.1基本类型, Universal
012 表示是应用定义的类型, Application
102 表示由上下文决定, Context-specific
112 表示私有的类型, Private
p/c, 第6位, 表示content的编码规则, 它的值有
0 表示基本类型
1 表示结构化类型
tag number的编码分两种情况
- 小于等于30时, 使用5个bit来编码就够了, 此时识别码只有1个字节
- 大于等于31时, 这5个bit全部置为1, 然后它后面的字节为一个变长整数
比如, 将tag number=31进行编码, 结果为
x x x 1 1 1 1 1 0 0 0 1 1 1 1 1
2
长度
如果长度
的第一个字节为:
如果是0x80
, 表示content的长度不确定, 需要根据content的定义来解码content, 此时content之后需要跟随结束码
若不是0x80
, 则又分为单字节编码和多字节编码.
- 如果content的长度小于或等于127个字节, 则使用1个字节表示长度.
- 如果content的长度大于127个字节, 则长度的第1个字节的低7位表示长度占用的字节数(不包含第1个字节), 第1个字节的最高位bit8置为1. 随后的N个字节(包含所有的8bit)共同表示一个整数
假设长度为201, 它的二进制为11001001
2, 最后编码为
10000001 11001001
2
结束码
就是两个字节的0, 即0x00 0x00
content编码
由数据类型决定, 对于ASN.1定义的通用类型
根据p/c的值, content又分为基本类型
码和结构化类型
编码
- 结构化类型编码
把数据切分成多段, 分别编码. 解码时, 需要再把它们拼接起来 - 基本类型编码
不需要切分或拼接.
ASN.1通用数据类型的编码
对于基本类型class = 0
p/c多数情况下为0, 有些情况下为1
boolean
tag = 1, p/c = 0
例如
True 0x01 0x01 0x00
False 0x01 0x01 0xFF
integer和enum
tag = 2, p/c = 0
content即为整数的字节数据. 但是有个限制条件
第1字节的8个bit和第2字节的最高bit, 共9个bit, 要满足
- 不能全为1
- 不能全为0
real
tag = 9 , p/c = 0
Real可以表示任意的实数. (ASN.1中没有虚数)
它的content有两种编码方式.
二进制编码
表示形式
R = S * N * 2F * Bexp
S为符号
N为正整数
F为缩放系数
exp为指数
content
的格式为
|---------第1字节------------------|
8 7 6 5 4 3 2 1
1 S指示符 B指示符 F exp格式 exp N
bit7为1时, S=-1; 为0时, S = 1
bit6 bit5 的取值有
002, B = 2
012, B = 8
102, B = 16
112, 保留
exp格式的取值有
002, 第2字节为指数
012, 第2, 3字节为指数
102, 第2, 3, 4字节为指数
112, 第2字节为指数区的长度, 取第2字节之后的这个长度的数据都是指数
content剩下的字节都是N
十进制编码
content第1字节的bit8,bit7为00
bit6到bit1的取值有
00 0001
2, 表示ISO 6093 NR1形式的数字, 如1234
, -12346
00 0010
2, 表示ISO 6093 NR2形式的数字, 如1.2
, 0.12
, -0.12
00 0011
2, 表示 ISO 6093 NR3形式的数字, 即科学记数法, 例如+0.56e+4
octetstring
tag = 4
用于编码任意的字符串, 不管内容是什么. 有两种编码方式
- 基本类型编码
非常简单, 就是
OctetString识别码 长度 字符串内容
- 结构化类型编码
其实就是把一个长的字符串拆分成了多次编码, 也有两种方式
OctetString识别码 长度
OctetString识别码 字符串长度 字符串
OctetString识别码 字符串长度 字符串
或
OctetString识别码 0x80
OctetString识别码 字符串长度 字符串
OctetString识别码 字符串长度 字符串
0x00 0x00
string
我们知道字符串是有字符集的, 因ASN.1定义不好几种tag
限制字符集的字符串
对于不同的字符集, 对应的tag值也不同
字符串类型 | tag值 | 说明 |
---|---|---|
UTF8String | 12 | 如字面意思, UTF8编码的字符串 |
NumericString | 18 | 由数字和空格组成的字符串 |
PrintableString | 19 | 字母, 数字, 空格, 以及'()+,-./:=? 组成的字符串 |
VisibleString | 26 | ISO/IEC 646中的字符, 和 空格, 相当于ascii的一子集 |
其它的一些诡异的字符集, 我也不知道是什么, 需要的时候再找文档吧
编码方式和octetstring非常类似
- 基本类型编码
非常简单, 就是
识别码 长度 字符串内容
- 结构化类型编码
其实就是把一个长的字符串拆分成了多次编码, 也有两种方式
识别码 长度
OctetString识别码 字符串长度 字符串
OctetString识别码 字符串长度 字符串
或
识别码 0x80
OctetString识别码 字符串长度 字符串
OctetString识别码 字符串长度 字符串
0x00 0x00
bitstring
tag = 3
bitstring需要按8bit对齐, 不足8bit的, 需要在末尾补0, content的编码方式为
无符整数(末尾补0的数量) bitstring
例如, 十六进制的ABC
, 编码为04ABC0
, 04
表示末尾补了4个bit的0
null
tag = 5
固定值0x05 0x00
sequence
sequence类型, 可以理解为C语言中的结构体
tag = 16, p/c=1, 只能使用结构化
编码方式
content为每个成员按照定义依次编码, 例如
定义:
SEQUENCE {name IA5String, ok BOOLEAN}
值:
{name “Smith”, ok TRUE}
编码结果为
Sequence Length Contents
0x30 0x0A
IA5String Length Contents
0x16 0x05 "Smith"
Boolean Length Contents
0x01 0x01 0xFF
sequence-of
sequence-of相当于sequence的特殊, 即所有成员都是相同的类型, 也可以理解为数组
编码方式和sequence一样
set
类似于sequence, 但是set对成员的顺序没有要求
编码方式与sequence一样
set-of
类似于sequence-of, 但对顺序没有要求
choice
选中了哪个值, 就将哪个值编码
prefixed type
prefixed type类似于C语言中的typedef. X.680中有详细的定义
这是简化之后的样式
PrefixedType ::= Type
PrefixedType ::= [ UNIVERSAL | APPLICATION | PRIVATE TagNumber ] IMPLICIT Type
PrefixedType ::= [ UNIVERSAL | APPLICATION | PRIVATE TagNumber ] Type
或者
PrefixedType ::= [ TagNumber ] IMPLICIT Type
,
PrefixedType ::= [ TagNumber ] Type
,
此时class=Context-specific
编码规则
- 如果定义是
PrefixedType ::= Type
那就就按照Type进行编码. - 否则, 如果声明中出现了
IMPLICIT
如果Type是基本类型编码
, 则PrefixedType也使用基本类型编码
如果Type是结构化编码
, 则PrefixedType也使用结构化编码
, 而且contents为Type编码之后的数据 - 否则, 如果声明中没有出现
IMPLICIT
则强制使用结构化编码
, contents为Type编码之后的数据
例如, 以字符串"Jones"举例
4.
Type1 ::= VisibleString
这是PrefixedType ::= Type
格式, 则直接使用VisibleString编码.
VisibleString Length Contents
0x1A 0x05 0x4A6F6E6573
Type2 ::= [APPLICATION 3] IMPLICIT Type1
, 编码结果为
定义中出现了IMPLICIT
, Type1为基本编码
, 所以Type2 也使用基本编码
class= 012, tag = 3
[Application 3] Length Contents
0x43 0x05 0x4A6F6E6573
Type3 ::= [2] Type2
定义中没有出现IMPLICIT
, 强制使用结构化编码
class默认为context-specific
, 即102, tag = 2, p/c=1
contents为Type2编码
[Application 3] Length Contents
0x42 0x07
[Application 3] Length Contents
0x43 0x05 0x4A6F6E6573
Type4 ::= [APPLICATION 7] IMPLICIT Type3
定义中出现了IMPLICIT
, Type1为结构化编码
, 所以Type4 也使用结构化编码
class=012, tag = 7, p/c=1
[Application 7] Length Contents
0x67 0x07
[Application 3] Length Contents
0x43 0x05 0x4A6F6E6573
Type5 ::= [2] IMPLICIT Type2
定义中出现了IMPLICIT
, Type2为基本编码
, 所以Type5 也使用基本编码
class=102, tag = 2, p/c=0
[2] Length Contents
0x82 0x05 0x4A6F6E6573
instance-of
instance-of用于编码类型
, 使用sequence编码
object identifier
http://www.oid-info.com 上查询已经定义了的object identifier
object identifier由3部分组成
{X Y Z}
将X,Y按以下公式编码成一个值
N = 40X +Y
然后将N, Z分别使用变长整数
编码, 得到content.
例如
{joint-iso-itu-t 999 3}
joint-iso-itu-t的值为2, 即
{2 999 3}
N = 240 + 999 = 1079, 编码为0x8837
Z = 3, 编码为0x03
class=6, p/c=0
最后结果为
OBJECT IDENTIFIER Length Contents
0x06 0x03 0x883703
时间相关
Time
使用UTF8String编码, Time中不能包含引号
Date
使用UTF8String编码, Time中不能包含引号和中横线
IME-OF-DAY
使用UTF8String编码, Time中不能包含引号和冒号
DATE-TIME
使用UTF8String编码, Time中不能包含引号, 冒号, 中横线, 以及大写字母T
DURATION
使用UTF8String编码, Time中不能包含引号, 以及大写字母T
实际应用
RSA的公钥和私钥的ASN.1编码
https://blog.csdn.net/wzj_whut/article/details/86477568