OSI 定义抽象对象的方法称为 ASN.1(Abstract Syntax Notation One,X.208),把这些对象转换成“0”和“1” 的比特流的一套规则称为 BER(Basic Encoding Rules , X.209)。
ASN.1 是一套灵活的记号,它允许定义多种数据类型,从 integer、 bit string 一类的简单类型到结构化类型,如 set 和 sequence,还可以使用这些类型构建复杂类型。
BER 说明了如何把每种 ASN.1 类型的值编码为 8bit 的 octet 流。 通常每个值有不止一种的 BER 编码方法。
一般使用另外一套编码规则 DER,它是 BER 的一个子集,对每个 ASN.1 值只有唯一一种编码方法。
本文主要是通过描述 ASN.1、 BER 和 DER 的一套子集,实现一种基于 OSI 的应用 —— RSA 公司的 Public-Key Cryptography Standards。
1、抽象语法符号 ASN.1
在 ASN.1 中, 一个类型就是一个值的集合。
有些类型有有限个值, 有些则有无限多个。
一个给定的 ASN.1 类型的值是该类型集合里的一个元素。
ASN.1 有四种类型:
简单类型:它相当于原子,没有组件;
结构类型:有组件;
标签类型,由其他类型生成;
其他类型,包括 CHOICE 和 ANY 类型。
可以使用 ASN.1 的分配符(::=)给类型和值指定名字,这些名字可以用于定义其他类型或值。
除了 CHOICE 和 ANY 类型以外,每种 ASN.1 类型都有一个标签,由一个类和一个非负的标签数组成。标签值可以唯一区分 ASN.1 类型。也就是说,ASN.1 类型的名字并不影响它的抽象含义,只有标签才有这个作用。
有四类标签:
Universal:该类型的含义在所有的application中都相同。这种类型只在X.208中定义
Application:该类型的含义由 application 决定, 如 X.500 目录服务。两个不同的 application 中的类型可以具有相同的application-specific标签但是不同的含
义。
Private:该类型的含义根据给定的企业而不同
Context-specific:该类型的含义根据给定的结构类型而不同 Context-specific 标签用于在一个给定的结构类型上下文中区分使用相同的下层标签的组件类型
在两个不同的结构类型中组建类型可以具有相同的标签但是含义不同。具有 universal 标签的类型在 X.208 中定义, X.208 也给出了类型的 universal 标签值。 使用其他标签的类型在很多地方都有定义,通常是通过 implicit 或 explicit 标签获得。
表一列出了部分 ASN.1 类型及其 universal-class 标签:
1.1、简单类型
简单类型没有子组件, 是“原子级” 的类型。 ASN.1 定义了几个简单类型,其中与PKCS
标准有关的类型如下:
z BIT STRING:由0和1任意组成的比特流
z IA5String:由IA5(ASCII)字符任意组成的字符流
z INTEGER:一个任意的整数
z NULL:null值
z OBJECT IDENTIFIER:对象标识符,有一列整数构成,用于确定对象,如算法或属性类型
z OCTET STRING:任意的octet(8 bit值)流
z PrintableString:任意可打印字符流
z T61String:T.61(8bit)字符的任意流
z UTCTime:"coordinated universal time"或者格林威治平均时(GMT)值。
简单类型分为两类:string 类型和 non-string 类型。 BIT STRING, IA5String, OCTET STRING, PrintableString, T61String, 和 UTCTime 是 string 类型。考虑到编码, String 类型可以视为由组件组成, 组件是 substring。
这样即使事先不知道值的长度也可以使用结构化的、 不定长的编码方式进行编码(例如, 从一个 file stream 中输入的 octet string 值)。String 类型可以指定大小限制,以限制值的长度。
1.2、结构类型
结构类型由组件组成。 ASN.1 定义了四种,都与 PKCS 标准有关:
z SEQUENCE:一个或多个类型的有序集合
z SEQUENCE OF:0个或某个给定类型多次出现的有序集合
z SET:一个或多个类型的无序集合
z SET OF:0个或某给定类型多次出现的无序集合
结构类型允许有可选组件。可选组件可能有默认值。
1.3、隐式和显式标签类型
在一个 application 中标签(tagging)对于区分类型十分有用, 标签通常也用于在一个结构类型中区分组件类型。例如,SET 或 SEQUENCE 类型的可选组件一般都给予不同的context-specific 标签以避免混淆。
有两种方法可以标记一个类型:隐式(implicitly)和显式(explicitly)。隐式标签类型是在其它类型基础上通过改变其下层类型的标签而生成的。隐式标签使用 ASN.1 关键词[class number] IMPLICIT表示。
显式标签是在其他类型基础上通过在其下层类型的标签之外添加一个外层标签而生成的。从效果上看,显式标签类型是包含一个组件的结构类型,该组件即下层类型。 显式标签由 ASN.1 关键词[class number] EXPLICIT表示。
ASN.1 中的其它类型包括 CHOICE 和 ANY 类型。 CHOICE 类型表示一个联合体, 它具有一个或多个备选项(alternative); ANY 类型表示任意类型的任意值,其中任意类型可能在使用对象识别符或整数值注册中定义。
2、基本编码规则 BER
ASN.1 的基本编码规则定义了一种或多种把任意 ASN.1 值表示成字节字符串的方法,缩写为 BER。
使用 BER,一个 ASN.1 的值有三种编码方法,选择哪种取决于值的类型和值的长度是否已知。
这三种方法是:
简单定长编码,结构化定长编码,及结构化不定长编码。
简单的 non-string 类型使用第一种(简单定长编码);
结构化类型可使用任一种结构化的编码方法;
简单的 string 类型根据值的长度是否已知可使用任一种方法。
隐式标签定义的类型可使用下层类型的方法,显式标签定义的类型使用结构化的编码方法。
每种 BER 编码方法都有三或四部分:
z Identifier octets:定义了 ASN.1 值的类和标签值,指明编码方法是简单的还是结构化的
z Length octets:对于定长编码方法,它指出了内容字节个数;对于结构化非定长编码方法,它指明长度是不确定的
z Contents octets:对于简单定长编码方法,它给出了值的具体表示;对于结构化的方法,它给出了值内容的BER编码的串联
z End-of-contents octets:对于结构化非定长的编码方法,它表示内容结束;对于其它方法,没有该部分。
2.1、简单定长方法
这种方法用于简单类型及通过对简单类型使用隐式标签生成的类型。它要求值的长度是事先预知的。
BER 编码的部分定义如下:
- Identifier octets, 有两种形式: 较小的标签值(标签值在 0 和 30 之间) 和较大的标签值(标签值大于等于 31)
z Low-tag-number form: 一个字节。 Bit8和bit7表示类(如表2), bit6值为0,表示编码方法为简单化的。 Bit5-1给出了标签值。
z High-tag-number form: 两个或多个octet。第一个octet形式如low-tag-number form,但是bit5-1均为1。第二个和以后的字节给出标签值,基于128,最高位在先,以便使用尽可能少的数字,除了最后一个字节以外, 每个字节的bit 8都置为1,最后一个字节的为0。
- Length octets:有两种格式:短型(长度在 0 至 127 之间)和长型(长度在 0 至 21008−1之间)
z Short form: 一个字节, bit8为0, bit 7-1表示长度。
z Long form: 2-127个字节。 第一个字节的Bit 8为1, bit 7-1表示后面有多少个用于表示实际长度的octet。 第二个和随后的octet给出实际长度,基于256,高位数字在先。
- Contents octets: 给出了值的具体表示(如果类型是由隐式标签定义的, 则给出了下层类型的值)。
2.2、结构化定长方法
结构化定长方法适用于简单的 string 类型、 结构类型、 在二者基础上通过隐式标签生成的类型和在任何类型基础上由显式标签生成的类型。 要求值的长度事先已知。
BER 编码方法各部分如下:
- Identifier octets:bit6 的值为 1,表示编码方法是结构化的。
- Length octets
- Contents octets,值的组件的 BER 编码的串联
z 对于简单string类型和在其基础上由隐式标签生成的类型, 是值的连续子串的BER编码的串联(隐式标签的下层值)
z 对于结构类型和在其基础上由隐式标签生成的类型, 是值的组件的BER编码的串联(隐式标签的下层值)
z 对于在任何类型基础上使用显式标签生成的类型,是下层值的BER编码
2.3 结构化非定长方法
结构化的、 非定长编码用于简单 string 类型、 结构类型、 在二者基础上使用隐式标签生成的类型和在任何类型基础上使用显式标签生成的类型。不要求事先知道值的长度。
BER 编码各部分如下:
z Identifier octets
z Length octets.一个字节,值为 80
z Contents octets
z End-of-contents octets 两个字节,为 00 00。
3、可辨别编码规则 DER
DER 是 BER 的子集,它定义了使用一个 octet string 来表示任何 ASN.1 值的编码方法。DER 用于需要使用唯一的 octet string 编码的应用程序,例如根据一个 ASN.1 编码来计算数字签名。
DER 在 BER 规则基础上增加了如下限制:
- 如果长度在 0 - 127 之间,必须使用短型长度表示法;
- 如果长度大于等于 128,必须使用长型长度表示法,并且长度必须使用尽可能少的字节表示;
- 对于简单 string 类型和在其基础上使用隐式标签生成的类型,必须使用简单定长编码方法;
- 对于结构化类型和在其基础上使用隐式标签生成的类型及在任何类型基础上使用显式标签生成的类型,必须使用结构化定长编码方法。
卫朋
人人都是产品经理受邀专栏作家,CSDN 嵌入式领域新星创作者、资深技术博主。2020 年 8 月开始写产品相关内容,截至目前,人人都是产品经理单渠道阅读 56 万+,鸟哥笔记单渠道阅读200 万+,CSDN 单渠道阅读 210 万+,51CTO单渠道阅读 180 万+。
卫朋入围2021/2022年人人都是产品经理平台年度作者,光环国际学习社区首批原创者、知识合作伙伴,商业新知 2021 年度产品十佳创作者,腾讯调研云2022年达人榜第三名。
文章被人人都是产品经理、CSDN、华为云、运营派、产品壹佰、鸟哥笔记、光环国际、商业新知、腾讯调研云等头部垂直类媒体转载。文章见仁见智,各位看官可策略性选择对于自己有用的部分。