ASN.1 常见问题解答




选择 ASN.1 的原因是什么?

选择 ASN.1 的一些原因是:

  • ASN.1 允许实现者选择最适合他们的任何编程语言,并在该语言中选择最适合其应用程序的数据类型的绑定。 例如,如果 ASN.1 将类型定义为项目的集合,您可以自由地将其表示为链表、数组等,这取决于最适合您的语言/应用程序的方式。
  • ASN.1 允许您以这样一种方式定义消息,即如果将来向消息中添加新字段,您不理解新字段的旧应用程序将继续与理解新字段的新应用程序一起正常工作。 这样您就不必同时将所有实现切换到消息的新版本。 对于此类消息,较旧的实现意识到他们应该期望并忽略消息中的新字段。
  • ASN.1 允许您对消息中的字段施加约束。 例如,您可以指示整数类型应该只携带值 1、2、7-10,或者字符串的长度应该在 20-30 个字节之间。
  • ASN.1 允许您表达消息字段之间的关系。 例如,您可以指示如果给定字段包含 7,则必须存在另一个字段。
  • ASN.1 允许您定义 OPTIONAL 字段,如果没有数据,则传输很少或不传输数据。
  • ASN.1 允许消息规范的作者(例如,标准编写者)以清晰简洁的方式向实现者明确指出消息中字段的性质。
  • ASN.1 让协议设计人员能够自由地描述消息的布局,而无需深入研究在两台机器之间传输的数据的位和字节细节,从而提高了生产力。
  • 通过使用正式的、可编译的符号 ASN.1 定义消息,可以通过使用将使用 ASN.1 描述的消息转换为 C、C++ 或 Java 等语言的工具以及编码器/解码器来提高生产力,以最小化或 无需弄清楚如何序列化数据以进行传输。


使用可扩展性是否有任何权衡?

如果您希望中继接收到的值(即,如果您希望解码器将意外值返回给您,而不是被解码器忽略),则使用可扩展性将导致生成稍微复杂的头文件。 在大多数情况下,它足以满足 “旧”版本忽略从“新”版本收到的扩展值,因为它通常不知道如何处理它们。

就您的应用程序代码而言,通常它不会导致更大或更复杂的代码。

如果您使用 PER,如果您使用类型可扩展性,则编码会稍大一些。 类型可扩展性对 BER 编码的大小没有影响。

除非您提前知道永远不需要扩展给定类型,否则您应该将其定义为可扩展的。


标记会影响 PER 中的编码数据吗?

一般来说,无论标签是什么,编码数据看起来都是一样的。

唯一的例外是选择类型的编码。 在 PER 中,选择类型的每个备选方案都由索引标识。 这些索引以取决于每个备选方案的标签的顺序分配给备选方案。 当使用自动标记时,索引确实对应于备选方案的定义顺序。


PER 中的开放类型值是如何编码的?

在PER中开放类型的编码与无约束的OCTET STRING类型的值相同。这意味着长度可以是一个或两个字节,或者如果长度是>16K字节,则编码会被分割开来,等等。


什么是 OER?

OER 代表八位字节编码规则。 OER 以 Rec 的形式出版。 ITU-T X.696 | ISO/IEC 8825-7,并且被设计为易于实施并产生比基本编码规则 (BER) 产生的编码更紧凑的编码。 除了减少开发手写编码器/解码器的工作量之外,使用 OER 还可以降低带宽利用率(尽管不如打包编码规则那么多)、节省 CPU 周期并降低编码/解码延迟。


什么是 ASN.1 及其编码规则?

国际标准组织 (ISO)、国际电工委员会 (IEC) 和国际电信联盟 - 电信部门 (ITU-T)(前身为国际电报电话咨询委员会 (CCITT))建立了抽象语法符号一( ASN.1) 及其编码规则作为描述和编码消息的标准。 ASN.1 是一种形式化的符号,用于抽象描述分布式计算机系统之间要交换的数据。 编码规则是一组规则,用于将 ASN.1 表示法中指定的数据转换为标准格式,该格式可由具有基于同一组规则的解码器的任何系统解码。


为什么命名位列表的存在会影响 PER 和 DER 中尾随零位是否被编码?

命名位列表影响尾随 0 位是否被编码,因为它们通常用于指定位图中特定位的语义。 例如,您可能有:

Capabilities ::= BIT STRING {eject(0), rewind(1), retension(2)}

这里每个 Capabilities 值的前三位具有相同的不同含义。 将此与没有命名位列表的 BIT STRING 进行对比,后者只是任意二进制数据的容器,其中特定位不带有预定义的语义。 例如:

BinaryString ::= BIT STRING

值 '1'B、'10'B 和 '100'B 的 Capabilities 值的语义是相同的(它们都表示支持弹出功能,但不支持倒带或重新张紧功能)。 此外,它们具有与“1000”B、“10000”B 等相同的语义,并且接收应用程序必须准备好接受这些值。 由于值 '1'B 与所有这些其他值具有相同的语义,PER & DER 要求您以最简单的形式对其进行编码,以满足对其施加的任何子类型约束,在本例中为单个位值“1”B。 这样,只有一种方法可以对具有命名位列表的 BIT STRING 进行编码。


如何在 PER 中对 CHOICE 扩展添加进行编码?

X.691(2008) 第 23.8 条规定 CHOICE 扩展附加值被编码为就好像它们是开放类型的值一样。 开放类型值的编码总是以后面的值的长度作为前缀。 PER 要求将 CHOICE 扩展附加值编码为开放类型值的原因是旧版本接收器将不知道编码值的类型定义。 因此,它总是在 CHOICE 扩展附加值的编码前加上前缀,因此接收的旧版本应用程序将知道如何跳过它不知道的类型的值。


我正在使用不定长度的编码形式。 为什么原始/构造位总是设置为 1?

这是因为对使用不定长度形式的限制要求不定长度编码总是被构造的。 简单类型的值,如 INTEGER、BOOLEAN 等,必须使用定长编码形式。 这确保了使用不定长度形式对构造的类型(例如 SEQUENCE、SET 等)进行安全编码,因为解码器始终可以正确确定内容结尾(两个零八位字节)出现的位置。


我是否必须手动将标签添加到具有相同 ASN.1 类型的 CHOICE 和 SET 组件? 或者有没有办法自动添加所需的标签?

您可以将 AUTOMATIC TAGS 关键字添加到模块定义语句中,以指示 ASN.1 工具自动添加所需的标签以进行组件区分。 特别是,当出现 AUTOMATIC TAGS 关键字时,OSS ASN.1 编译器会提供所有必要的标签。

以下摘录说明了 AUTOMATIC TAGS 关键字的使用:

ModuleName DEFINITIONS AUTOMATIC TAGS ::= BEGIN
ChoiceA ::= CHOICE {
a INTEGER,
b INTEGER,
c INTEGER
}
END

请注意上面 CHOICE 的元素如何没有应用任何上下文相关标签。


在解码 PER 编码的 PDU 时,IA5String 字符似乎被解码,就好像从它们的数值中减去了 1。 为什么?

这是编码器或解码器端 ASN.1 语法中的拼写错误导致的常见错误。 省略空格字符通常很容易。 例如假设在编码器端 IA 定义为:

IA ::= IA5String (FROM ("0123456789No.*,"))

但在解码器方面:

IA ::= IA5String (FROM ("0123456789No. *,")) <-- space is here

带有一个额外的空格字符“”。 这导致值

a IA ::= "1234"

在 PER 中被解码为“2345”而不是“1234”。 PER 中允许的字母表在值的编码/解码方式中起着至关重要的作用。


你能解释一下 UTF8String 以及它是如何编码的吗?

UniversalString 和 UTF8String 都支持完全相同的字符集,前 64K 字符都是 BMPString 中的字符集。 请注意,BMPString 的前 128 个字符与 IA5String 是同一组抽象字符(我们使用术语“抽象”来指出它们实际上是相同的,但它们的编码不同),并且由于 BMPString 是 UniversalString 和 UTF8String 意味着 IA5String 是这些字符串类型的前 128 个抽象字符。

好的,既然我们知道 UTF8String 不是由 BMPString 和 UniversalString 字符组成,而只是对与 BMPString 和 UniversalString 编码的字符集完全相同的一组字符进行编码的不同方式,那么让我们来谈谈它实际上是如何编码的。

简而言之,如果一个字符的第一个字节的第一位是 0,则意味着这个字符是一个字节长,如果你看一下字符映射你会看到这组字符(其中有 128 个, 自然)是美国 ASCII(即 IA5String)。

如果字符的前 3 位是 110,则表示该字符长 2 个字节,其值为 110xxxxxx 10xxxxxx,其中 x 是有效位,110 中的 11 表示字符长 2 个字节。

如果字符的前 4 位是 1110,则表示该字符长 3 个字节,其值为 1110xxxx 10xxxxxx 10xxxxxx,其中 x 是有效位,1110 中的 111 表示该字符长 3 个字节。

如果一个字符的前 5 位是 11110,则表示该字符长 4 个字节,其值为 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx,其中 X 是有效位,11110 中的 1111 表示该字符长 4 个字节。

如果字符的前 6 位是 111110,则表示该字符长 5 个字节,其值为 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx,其中 X 是有效位,111110 中的 11111 表示该字符长 5 个字节。

如果一个字符的前 7 位是 1111110,则表示该字符长 6 个字节,其值为 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx,其中 X 是有效位,1111110 中的 111111 表示该字符长 6 个字节。


扩展加法的 PER ALIGNED 编码的起始位应该是八位字节对齐还是八位字节不对齐?

它应该作为八位字节未对齐的位字段添加。
X.691(2008) 的第 19.7 和 19.8 节说,扩展添加的编码从位掩码开始,该位掩码的位指示特定扩展的存在。 反过来,位掩码以它的长度为前缀,根据 19.8,它被编码为“通常较小的长度”。
X.691(2008) 的第 11.9.3.4 节规定,“通常较小的长度”的编码从一个 0 或 1 的单个位位字段开始(如果扩展的数量 <=64 和 1,则为 0 除此以外)。
术语“位域”在 X.691(2008) 的第 3.7.3 节中进行了解释,随后是澄清说明:
注意:如果使用该术语后跟“对齐变体中的八位字节对齐”,这意味着在 PER 对齐变体的完整编码中,位字段需要从八位字节边界开始。
由于 X.691(2008) 的第 11.9.3.4 节没有明确提到单个位字段是八位字节对齐的,这意味着不需要八位字节边界上的对齐。

请参阅 X.691(2008) 的第 11.1.4 节,了解如何在构建完整编码时使用位域,而不是如何使用八位字节对齐的位域。


有没有办法跳过解码 BER 中的 SET/SEQUENCE 中的一些不需要的字段?

是的,您可以这样做,但只能在 BER/DER/CER 中,而不是在 PER/UPER 中,因为 PER 的性质。 考虑 BER/DER/CER 中的以下 ASN.1 语法:

X DEFINITIONS ::= BEGIN

S1 ::= SEQUENCE {
a BOOLEAN,
b INTEGER OPTIONAL,
c IA5String OPTIONAL,
d OCTET STRING OPTIONAL
}

S2 ::= SEQUENCE {
a BOOLEAN,
b INTEGER OPTIONAL,
...,
...,
d OCTET STRING OPTIONAL
}

s S1 ::= {
a TRUE,
b 25,
c "xx",
d '11'H
}

END

基于上述语法,您可以对 S1 PDU 进行编码,但使用 S2 PDU 对其进行解码,其类型利用 ASN.1 可扩展性。 第一个“...”标志着扩展的开始,第二个标志着它的结束。 第二个“...”之后的字段 d 继续扩展根。 在 BER/DER/CER 中,解码器将简单地跳过两个扩展标记之间的所有字段并继续使用字段 d 进行解码。


为什么零填充出现在短约束受限字符串类型的 PER ALIGNED 编码中?

让我们考虑一下:

N ::= NumericString (SIZE(0..3))

n N ::= "27"

和:

N ::= NumericString (SIZE(0..4))

n N ::= "27"

X.691(2008) 的第 30.5.7 条说:
30.5.7 如果“aub”不等于“alb”或大于或等于 64K,则应调用 11.9 以添加前面的位字段 通过一个长度行列式,其中 n 作为字符串中字符的计数,长度行列式为“alb”,上限为“aub”。 如果“aub”乘以“b”大于或等于 16,则应将位字段添加为字段(对齐变体中的八位字节对齐),否则应添加为非八位字节的位字段 对齐。 这样就完成了本条的程序。

由于我们有 SIZE(0..4),我们计算:

aub * b == 4 * 4 = 16

这使我们在上限大于 3 时添加有问题的填充。


在类型定义和信息对象集中使用扩展标记有什么区别? 扩展标记是否不可见?

扩展标记就类型定义而言是不可见的,但就简单表约束和组件关系约束而言并非不可见。
类型本身是可扩展的,与限制它是可扩展的对象集之间是有区别的。 在类型是可扩展的情况下,它天生可以采用可扩展约束允许的任何值。 例如,

INTEGER (1..8, ...)

可以随时假设任何有效值。 将此与使用简单表约束进行约束的 INTEGER 类型进行对比,在这种类型中,此类类型只能假定在该类型被编码/解码时恰好包含在信息对象集中的那些值。 随着程序的运行,这可能会随着时间的推移而变化,因为可扩展信息对象集中的对象集可能会在运行时发生变化。
在 BER、DER 和 CER 的情况下,这种区别不太重要,其中类型的可扩展性在其编码方式中不发挥作用,但在 PER 中起主要作用。 在 PER 中,使用扩展标记“...”定义的类型的值使用 1 位前缀进行编码,当设置为 0 时,意味着后面的值在扩展根中,因此以优化的形式编码。 (例如,上面示例中的值 1-8 将被编码为 3 位)。 但是,当设置为 1 时,意味着后面的值以更通用的形式编码。 (例如,上例中不在 1-8 范围内的值占用 16 位或更多位)。


你能解释一下类型可扩展性在 PER 中是如何工作的吗?

考虑以下两个 ASN.1 语法定义:

A ::= SEQUENCE { --defined in v1
f1 BOOLEAN,
f2 BOOLEAN,
...,
}

A ::= SEQUENCE { --defined in v2
f1 BOOLEAN,
f2 BOOLEAN,
...,
e1 BOOLEAN OPTIONAL,
e2 BOOLEAN
}

类型可扩展性背后的目的是允许不理解新字段的 V1 应用程序接收具有它无法识别的字段的 V2 消息,并将它们视为由 V1 应用程序发送,同样,对于 V2 应用程序来说 接收缺少字段的 V1 消息。 如果 V2 应用程序收到缺少强制扩展添加的消息,它可以安全地假定该消息是由 V1 应用程序发起的。

只有在扩展附加位图中有一个位表示存在/不存在哪些扩展附加值时,才必须对扩展标记之后的强制字段进行编码。 因此,在强制扩展附加 y 之后定义了扩展附加 x,并且 x 的值存在于编码中,那么 y 的值必须存在。 此外,如果强制扩展附加 y 是 SEQUENCE 中的最后一个组件,并且在扩展附加位图中存在一个位,则该位必须设置为 1,因为该位的存在表明消息的发起者 知道这个扩展添加,因此它的存在是强制性的。 只有当消息是从未定义强制扩展添加的早期版本的消息定义中继时,才可以省略它(在这种情况下,扩展添加位图中将没有位)。 ITU-T 建议 X.680(2008) 25.15 注 2 中指出了这一点:

作为扩展添加但不包含在“ExtensionAdditionGroup”中的“ComponentType”如果未标记为 OPTIONAL 或 DEFAULT,则应始终对其进行编码,除非抽象值是从使用较早版本抽象语法的发送者中继的 其中未定义“ComponentType”。

换句话说,PER 将标记为 OPTIONAL 的扩展添加与非 OPTIONAL 的扩展添加完全相同。


GeneralString、GraphicString 等与 ASN.1 中其他广泛使用的字符串类型有何不同?

GeneralString、GraphicString、TeletexString 和 VideotexString 都具有在指定字符时允许转义序列的特性。 因此,这些类型之一中的字符可能占用一个八位字节,或两个,或三个......,并且每个字符的八位字节数对于给定的字符串值不一定是固定的,它可以变化。 将此与 IA5String、PrintableString、VisibleString、NumericString、BMPString 和 UniversalString 进行对比,它们都具有每个字符的固定位数,因此被称为已知乘数字符串类型或固定宽度字符串类型。 GeneralString 等是可变宽度字符串类型。


我是否需要对信息对象进行编码/解码?

不,您永远不会对信息对象进行编码或解码。 它们仅用于提供有关消息组件如何相关的信息,如何确定开放类型中值的类型等。


在 BER 中编码有符号和无符号 INTEGER 之间有什么区别吗?

BER 中的所有 INTEGER 都被编码为有符号整数。 请注意,整数值的前九位绝不是全 1 或全 0。


ASN.1中的标签号有什么限制吗?

ASN.1 对标签数量没有限制,但 NIST 稳定实施协议 (1991) 及其欧洲和亚洲同行将标签的大小限制为 16383。


ASN.1 中的 DEFAULT {} 是什么意思?

DEFAULT 通常意味着在语义上无法区分该值是否已编码。 一般来说,这意味着如果它是默认值,您可以选择省略该值,尽管一些编码规则(例如,DER)要求如果该值是默认值,则永远不会被编码。

DEFAULT {} 仅对具有命名位列表、SET OF 和 SEQUENCE OF 的 BIT STRING 有效。 在 BIT STRING 的情况下,它表示默认值为空字符串(长度为 0),而在 SET OF 和 SEQUENCE OF 的情况下,它表示出现 0 次的值。


我可以用 DER 编码,然后用 BER 解码吗?

是的你可以。 所有有效的 DER 编码都是有效的 BER 编码,但并非总是如此。


什么是规范编码规则 (CER)?

它与 BER 类似,因为所有有效的 CER 编码都是有效的 BER 编码。 BER 允许以多种方式对大多数值进行编码,而 CER 规定对于给定值仅允许使用其中一种方式(例如,BER 表示对于 BOOLEAN,值 00 为 FALSE,任何非零值均为 TRUE,而 CER 表示 00 为 FALSE,FF 为 TRUE,值 01-FE 为 false)。 它在大多数方面类似于 DER,因为 DER 还规定了一种编码任何给定值的方法。 它们最大的不同之处在于:

1. DER 使用定长编码,而 CER 使用不定长编码。

2. DER 要求字符串类型以原始形式编码,而 CER 要求字符串类型在长度小于 1000 个八位字节时以原始形式编码,并且以具有 1000 个字节段的构造形式(可能除了最后一句) 如果它们的长度超过 1000 个字节。

3. 在 DER 中,SET 的组件必须在运行时进行排序。 在 CER 中,使用与 PER 中相同的算法,基于标签对 SET 的组件进行预排序。


OPTIONAL 元素在扩展添加中的意义是什么?

考虑类型:

MySeq ::= SEQUENCE {
i INTEGER,
...,
e1 BOOLEAN,
e2 INTEGER (0..65535) OPTIONAL
}

扩展添加项对于未定义这些项的实现版本都是“可选的”(例如,在 MySeq 的版本 1 中,未定义 e1 和 e2),但在定义了扩展添加的实现版本中(例如, 假设 e1 和 e2 在版本 2 中定义)未标记为 OPTIONAL 的扩展添加项对于该版本是强制性的,而那些标记为 OPTIONAL 的项对于该版本是可选的。 因此,如果上面的 e1 和 e2 在版本 2 中定义但不是在版本 1 中定义,则版本 2 实现需要始终传输 e1,如果它是发起消息的,因为它没有标记为 OPTIONAL,但可以省略 e2。 如果它不是发起消息(例如,它正在转发从版本 1 实现接收到的消息),那么如果 e1 和 e2 不存在于消息中,则可以随意省略它们。 这意味着如果 e1 不存在,您将永远无法使 e2 出现在消息中。