ASN.1 BER编码


官方文档
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, 它的二进制为110010012, 它的变长编码为
10000001 010010012

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的编码分两种情况

  1. 小于等于30时, 使用5个bit来编码就够了, 此时识别码只有1个字节
  2. 大于等于31时, 这5个bit全部置为1, 然后它后面的字节为一个变长整数

比如, 将tag number=31进行编码, 结果为
x x x 1 1 1 1 1 0 0 0 1 1 1 1 12

长度

如果长度的第一个字节为:
如果是0x80, 表示content的长度不确定, 需要根据content的定义来解码content, 此时content之后需要跟随结束码
若不是0x80, 则又分为单字节编码和多字节编码.

  1. 如果content的长度小于或等于127个字节, 则使用1个字节表示长度.
  2. 如果content的长度大于127个字节, 则长度的第1个字节的低7位表示长度占用的字节数(不包含第1个字节), 第1个字节的最高位bit8置为1. 随后的N个字节(包含所有的8bit)共同表示一个整数
    假设长度为201, 它的二进制为110010012, 最后编码为
    10000001 110010012

结束码

就是两个字节的0, 即0x00 0x00

content编码

由数据类型决定, 对于ASN.1定义的通用类型
根据p/c的值, content又分为基本类型码和结构化类型编码

  1. 结构化类型编码
    把数据切分成多段, 分别编码. 解码时, 需要再把它们拼接起来
  2. 基本类型编码
    不需要切分或拼接.

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. 不能全为1
  2. 不能全为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 00012, 表示ISO 6093 NR1形式的数字, 如1234, -12346
00 00102, 表示ISO 6093 NR2形式的数字, 如1.2, 0.12, -0.12
00 00112, 表示 ISO 6093 NR3形式的数字, 即科学记数法, 例如+0.56e+4

octetstring

tag = 4
用于编码任意的字符串, 不管内容是什么. 有两种编码方式

  1. 基本类型编码
    非常简单, 就是
    OctetString识别码 长度 字符串内容
  2. 结构化类型编码
    其实就是把一个长的字符串拆分成了多次编码, 也有两种方式
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非常类似

  1. 基本类型编码
    非常简单, 就是
    识别码 长度 字符串内容
  2. 结构化类型编码
    其实就是把一个长的字符串拆分成了多次编码, 也有两种方式
识别码   长度  
                   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

编码规则

  1. 如果定义是PrefixedType ::= Type
    那就就按照Type进行编码.
  2. 否则, 如果声明中出现了IMPLICIT
    如果Type是基本类型编码, 则PrefixedType也使用基本类型编码
    如果Type是结构化编码, 则PrefixedType也使用结构化编码, 而且contents为Type编码之后的数据
  3. 否则, 如果声明中没有出现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 = 2
40 + 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

猜你喜欢

转载自blog.csdn.net/wzj_whut/article/details/86241935