ASN.1 编码规则

ASN.1 编码规则

ASN.1Abstract Syntax Notation One) 是一套标准,是描述数据的表示、编码、传输、解码的灵活的记法,它提供了一套正式、无歧义和精确的规则以描述独立于特定计算机硬件的对象结构。ASN.1本身只定义了表示信息的抽象语法,但是没有限定其编码的方法。

asn.1的标准编码规则有以下几种:

  1. 基本编码规则(BER,Basic Encoding Rules
    1. 规范编码规则 (CER Canonical Encoding Rules)
    2. 唯一编码规则(DER,Distinguished Encoding Rules)
    3. 压缩编码规则(PER,Packed Encoding Rules
    4. XML编码规则(XER,XML Encoding Rules

描述ASN.1记法的标准:

  1. ITU-T Rec. X.680 | ISO/IEC 8824-1
    1. ITU-T Rec. X.681 | ISO/IEC 8824-2
    2. ITU-T Rec. X.682 | ISO/IEC 8824-3
    3. ITU-T Rec. X.683 | ISO/IEC 8824-4

描述ASN.1编码规则的标准

  1. ITU-T Rec. X.690 | ISO/IEC 8825-1 (BER, CER and DER)
    1. ITU-T Rec. X.691 | ISO/IEC 8825-2 (PER)
    2. ITU-T Rec. X.692 | ISO/IEC 8825-3 (ECN)
    3. ITU-T Rec. X.693 | ISO/IEC 8825-4 (XER)

BER CER DER 编码规则

X.690

X.690 是一个ITU-T(https://en.wikipedia.org/wiki/ITU-T)标准,指定了几种编码规则,主要是(BER, CER, DER

基本编码规则 (BER) 是 ASN.1 标准制定的用于将数据编码为二进制格式的原始规则。这些规则在 ASN.1 术语中统称为传输语法,指定用于编码数据的确切八位字节(8 位字节)

BER编码规则

BER 基本编码规则的格式指定了一种用于编码 ASN.1 数据结构的自描述和自定界格式。每个数据元素都被编码为类型标识符、长度描述、实际数据元素,以及必要时的内容结束标记。这些类型的编码通常称为类型-长度-值(TLV) 编码。但是,在 BER 的术语中,它是identifier-length-contents。这种格式允许接收者从不完整的流中解码 ASN.1 信息,而无需预先了解数据的大小、内容或语义.

CER 和DER 都是BER的一种变体,他们从基本编码规则允许的编码中选择一种编码,消除了其余选项。
BER编码的数据由以下几个部分组成:

Identifier octets Type Length octets Contents octets End-of-Contents octets
Type Length Value (only if indefinite form)

End-of-Contents只在编码不定长数据的时候才会使用,用于标记数据内容的结束。
Value在 NULL类型的时候也可以省略

Identifier octets Type

type 被编码成一个或多个字节,主要由 标签种类tag class,数据类型P(Primitive)/C(Constructed)tag number 组成。

结构如下:


image.png

第一个字节的前2位表示 tag class 第3位表示p/c,后边则是tag number

  1. tag class 有以下几种,它的值是用来区分 ASN.1 类型
类型 说明
universal 0 表示原始的数据类型
application 1 只适用于一个特定的应用程序类型
context-specific 3 根据上下文定义的类型
private 4 私人规范中定义的类型
  1. p/c 表示数据内容是基本的数据类型还是复合的数据类型
类型 数值 说明
Primitive (P) 0 数据内容仅由一个数据元素组成
Constructed (C) 1 数据内容由多个数据元素组成
  1. tag number 数据的标识,是固定的。在asn.1的原始数据类型表里可以看到。
    如果定义的数据类型不是 Universal 的数据类型,那么此时需要用到更多的字节序列如 Octet2。在使用这类标记的时候,要将 Octet1 的第 5 到第 1 个二进制位置为 1 ,如果 Octet2 后面还有 Octet3,那么 Octet2 的第 8 个二进制位应该为 1。

asn.1的原始数据类型

数据类型 编码类型 Tag number(十进制) Tag number(十六进制)
Decimal Hexadecimal
End-of-Content (EOC) Primitive 0 0
BOOLEAN Primitive 1 1
INTEGER Primitive 2 2
BIT STRING Both 3 3
OCTET STRING Both 4 4
NULL Primitive 5 5
OBJECT IDENTIFIER Primitive 6 6
Object Descriptor Both 7 7
EXTERNAL Constructed 8 8
REAL (float) Primitive 9 9
ENUMERATED Primitive 10 A
EMBEDDED PDV Constructed 11 B
UTF8String Both 12 C
RELATIVE-OID Primitive 13 D
TIME Primitive 14 E
Reserved 15 F
SEQUENCE and SEQUENCE OF Constructed 16 10
SET and SET OF Constructed 17 11
NumericString Both 18 12
PrintableString Both 19 13
T61String Both 20 14
VideotexString Both 21 15
IA5String Both 22 16
UTCTime Both 23 17
GeneralizedTime Both 24 18
GraphicString Both 25 19
VisibleString Both 26 1A
GeneralString Both 27 1B
UniversalString Both 28 1C
CHARACTER STRING Constructed 29 1D
BMPString Both 30 1E
DATE Primitive 31 1F
TIME-OF-DAY Primitive 32 20
DATE-TIME Primitive 33 21
DURATION Primitive 34 22
OID-IRI Primitive 35 23
RELATIVE-OID-IRI Primitive 36 24

举个栗子:
以私钥签名的结构体为例
数据类型是SEQUENCE,那么根据表中对应的编码类型为Constructed
tag calss 是Universal
tag number 是16,转换为2进制是 10000
编码后的type应该是 0011 0000 结果就是0x30

      DigestInfo ::= SEQUENCE {
           digestAlgorithm AlgorithmIdentifier,
           digest OCTET STRING
      }

Length octets

Length字段标识 value字段编码 的字节数,分为定长和不定长。其中定长的length指的是长度不超过 127 的短格式和长度超过 127 字节的长格式。

image.png

定长的短格式:

  1. 前面的第一位是0

    1. 后边的7个位代表长度,因为7个位的2进制是0100 0000,最大也就是127,所以要求短格式不能超过127

    定长长格式:

  2. 最高位是1,后面的7个位表示长度值占用的字节数,然后跟上长度值。

    1. 例如长度为300,转换成2进制是0000 0001 0010 1100,占用2个字节,那么编码后应该是
      1 000 0010 0000 0001 0010 1100 总共占用3个字节,用16进制表示 0x82 0x01 0x2c

不定长格式:

  1. 根据图上看到最高位为1,后边7个位全是0,那就是1000 0000 也就是0x80 固定的数值
    1. value结尾处标记2个0x00代表内容结束,因为是不定长,必须知道内容在网络上传输时读到啥时候结束。

保留格式

  1. 最高位为1,后边7个为全是1,0xFF表示
  2. 同时在数据内容结尾处用2个0x00标记,代表着内容结束。

Contents octets

value 是数据内容的字节编码,如果不存在或者是虚对象的时候可能没有,比如value是NULL

DER(Distinguished Encoding Rules)

DER 适用于需要唯一编码的情况,例如在密码学中,并确保需要数字签名的数据结构产生唯一的序列化表示。DER 可以被认为是 BER 的规范形式。DER编码主要是为满足 X.509 规范的安全数据传输的要求而创建的。

DER 在 BER 规则基础上增加了如下限制:
1. 如果长度在 0 - 127 之间,必须使用短型长度表示法,definite-Short-form;
2. 如果长度大于等于 128,必须使用长型长度表示法,并且长度必须使用尽可能少的字节表示;
3. 对于简单 string 类型和在其基础上使用隐式标签生成的类型,必须使用简单定长编码方法;
4. 对于结构化类型和在其基础上使用隐式标签生成的类型及在任何类型基础上使用显式标签生成的类型,必须使用结构化定长编码方法。

举个例子
这是之前用私钥签名der编码的数据

      DigestInfo ::= SEQUENCE {
           digestAlgorithm AlgorithmIdentifier,
           digest OCTET STRING
      }
      
      AlgorithmIdentifier  ::=  SEQUENCE  {
           algorithm OBJECT IDENTIFIER,
           parameters ANY DEFINED BY algorithm OPTIONAL  
      }
        oidSHA256 := asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1}
      
        // 算法标识符
        type AlgorithmIdentifier struct {
            Algorithm  asn1.ObjectIdentifier
            Parameters asn1.RawValue `asn1:"optional"`
        }
      
        // 签名的数据结构
        type DigestInfo struct {
            DigestAlgorithm AlgorithmIdentifier
            Digest          []byte
        }
      
        sha := sha256.New()
        m := []byte{50}
        sha.Write(m)
        h := sha.Sum(nil)
      
        var digestInfo = DigestInfo{
            DigestAlgorithm: AlgorithmIdentifier{
                Algorithm: oidSHA256,
                Parameters: asn1.RawValue{
                    Tag: asn1.TagNull,
                },
            },
            Digest: h,
        }
      
        d, err := asn1.Marshal(digestInfo)
        if err != nil {
            fmt.Println(err)
            return
        }
        oid := hex.EncodeToString(d)
        fmt.Println(oid)
          // 3031300d060960864801650304020105000420d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35
30 31
    30 0d
        0609608648016503040201
        0500
    0420           d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35
  1. 第一个字节30代表Identifier octets Type,转换成2进制是 00110000,我们计算下type,前2位是tag class,00110000 >> 6 = 00 代表universal,对照表SEQUENCE是原始数据类型,所以是OK的。第三个位是1代表是复合数据类型,也就是由多个元素构造的数据类型Constructed,那后边的10000`自然是tagNumber了,转换成十进制是16刚好与上表对应。

  2. 第二个字节31 代表 Identifier octets length,转换成2进制是00110001,通过编码后的数据,我们可以看出长度是小于127的(可以用0x31 & 0x7F == 0x31,如果是true,说明是短格式,否则是长格式,如果是长格式那0x31 & 0x7F = 长度占用了几个字节,那紧接着往后的这几个字节组成的就代表长度值本身),而DER编码长度在 0 - 127 之间,使用的是定长的短长度编码,第一位是0,后边7个位代表长度,那么长度=00110001 也就是31。

  3. 第三个字节还是30,因为是SEQUENCE里面套了个SEQUENCE,所以30代表的是AlgorithmIdentifier结构体的type。

  4. 第四个字节同理0d代表的是长度,AlgorithmIdentifier这个的长度,0x0d==十进制的13所以13就是长度,短长度。

  5. 第五个往后13个字节0609 6086480165030402010500就代表AlgorithmIdentifier这个的数据内容

    1. 这13个字节其中有11个字节0609608648016503040201是算法标识符Algorithm
      • 0609,是type和长度,后边的是数据内容,06 转换为二进制0000 0110,前2位00是tag class 第三位0代表不是构造的复合数据类型,0 0110就是tagnumber,也就是6。
      • 608648016503040201 就是sha256算法标识符2.16.840.1.101.3.4.2.1DER编码后的值
    2. 05和00是值NULL的编码,我们在代码中给的值就是NULL,05这个字节指的是NULL的type,00指的是长度为0么。
  6. 第十四个字节04指的是Digest 它的类型是OCTET STRING,那tag class为 universal 值是0,基本数据类型primitive由一个元素组成值是0,tag number是4,组合起来就是00 0 00100,转16进制是0x04。

  7. 第十五个字节20代表了长度,指的是Digest这个字段的长度,用二进制表示0010 0000,短格式,长度就是32个字节。

  8. 剩下的d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35就是数据编码的内容了,在本示例中就是哈希值h的DER编码值
    ASN.1 JavaScript decoder中把DER编码后的值输入进去可以看到分析的结果图如下:

    image.png

参考
参考
参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352