11

我正在解码 ASN1(在 X.509 中用于 HTTPS 证书)。我做得很好,但是有一件事我找不到和可以理解的文档。

在这个JS ASN1 解析器中,您会在一个元素下看到 a[0]和 a ,第一个在 data: 中看起来像这样。我想知道这意味着什么以及如何解码。[3]SEQUENCEA0 03 02 01 02 ...

另一个例子是Anatomy of an X.509 v3 Certificate,前两个元素[0]后面有一个。SEQUENCE

我不明白的是 A0 如何适合标签字节的前 2 位是一个类,下一个是原始/构造位,其余 5 位应该是标签类型的方案。A0 是 10100000,这意味着标签类型值为零。

4

4 回答 4

16

听起来您需要介绍 ASN.1 标记。有两个角度可以解决这个问题。X.690 定义了 BER/CER/DER 编码规则。因此,它回答了标签如何编码的问题。X.680 定义了 ASN.1 本身。因此,它定义了标记的语法和规则。这两个规范都可以在 ITU-T 网站上找到。我会给你一个快速的概述。

BER/DER/CER 中使用标签来识别类型。它们对于区分 SEQUENCE 的组件和 CHOICE 的替代项特别有用。

标签结合了标签类别和标签编号。标记类是 UNIVERSAL、APPLICATION、PRIVATE 和 CONTEXT-SPECIFIC。UNIVERSAL 类基本上用于内置类型。APPLICATION 通常用于用户定义的类型。CONTEXT-SPECIFIC 通常用于构造类型(SEQUENCE、CHOICE、SEQUENCE OF)内的组件。在语法上,当在 ASN.1 模块中指定标签时,它们会写在括号内:[ tag_class tag_number ]; 对于 CONTEXT-SPECIFIC,tag_class 被省略。因此,[应用程序 10] 或 [0]。

虽然每个 ASN.1 类型都有一个关联的标签,但从句法上讲,还有一个“TaggedType”,ASN.1 作者使用它来指定用于编码类型的标签。基本上,TaggedType 将标签前缀放在类型之前。例如:

MyType ::= SEQUENCE {
  field_with_tagged_type [0] UTF8String
}

TaggedType 中的标记是显式的或隐式的。如果是显式的,这意味着我希望对原始标签进行显式编码。如果是隐式的,这意味着我很高兴只对我指定的标签进行编码。在显式情况下,BER 编码产生一个嵌套的 TLV(标签长度值):外部标签(上例中的 [0])、长度,然后是另一个 TLV 作为值。在示例中,此内部 TLV 将具有用于 UTF8String 的 [UNIVERSAL 12] 标记。

标记是显式的还是隐式的取决于您如何编写标记和标记环境。例如:

MyType2 ::= SEQUENCE {
  field_with_explicit_tag [0] EXPLICIT UTF8String OPTIONAL,
  field_with_implicit_tag [1] IMPLICIT UTF8String OPTIONAL,
  field_with_tag [2] UTF8String OPTIONAL
}

如果您既未指定 IMPLICIT 也未指定 EXPLICIT,则有一些规则定义标记是显式还是隐式(参见 X.680 31)。这些规则考虑了为 ASN.1 模块定义的标记环境。ASN.1 模块可以将标记环境指定为隐式标记、显式标记或自动标记。 粗略地说,如果您没有为标记指定 IMPLICIT 或 EXPLICIT,则如果标记环境为 EXPLICIT,则标记将是显式的,如果标记环境是 IMPLICIT 或 AUTOMATIC,则标记将是隐式的。自动标记环境与 IMPLICIT 标记环境基本相同,除了自动为 SEQUENCE 和 CHOICE 类型的成员分配唯一标记。

请注意,在上面的示例中,MyType2 的三个组件都是可选的。在 BER/CER/DER 中,解码器将根据编码标签(显然最好是唯一的)知道存在什么组件。

于 2013-02-25T17:19:44.590 回答
14

ASN.1 BER 和 DER 使用 ASN.1 TAGS 来明确识别编码流中的某些组件。ASN.1 标签有 4 类:UNIVERSAL、APPLICATION、PRIVATE 和 context-specific。[0] 是特定于上下文的标记,因为它前面没有标记类关键字。UNIVERSAL 为 ASN.1 中的内置类型保留。大多数情况下,您会看到特定于上下文的标签,以消除包含 OPTIONAL 元素的 SEQUENCE 中的潜在歧义。如果您知道您收到的两件不是可选的,一件接一件,即使它们的标签相同,您也知道哪个是哪个。但是,如果第一个是可选的,则两者必须具有不同的标签,否则如果编码中只有一个,您将无法分辨您收到的是哪一个。

今天大多数情况下,ASN.1 规范使用“自动标签”,因此您不必担心消息中的这种歧义消歧,因为 SEQUENCE、SET 和 CHOICE 的组件将自动获取以 [0] 开头的特定于上下文的标签,[ 1]、[2] 等。

您可以在http://www.oss.com/asn1/resources/books-whitepapers-pubs/asn1-books.html找到有关 ASN.1 标签的更多信息,其中有两本可免费下载的书籍。

另一个优秀的资源是http://asn1-playground.oss.com,您可以在其中尝试在线编译器和编码器/解码器中具有不同标签的 ASN.1 规范的变体。在那里您可以看到标签更改对编码的影响。

于 2013-02-25T16:43:28.590 回答
1

[0] 是特定于上下文的标记类型,这意味着要弄清楚它赋予字段(如果设置了“Constructed”标志)或数据值(如果未设置“Constructed”标志)它包装的含义;你必须知道它出现在什么上下文中。

此外,您还需要知道发送方和接收方在 DER 流中交换什么样的对象,即。“ASN.1 模块”。

假设他们正在交换证书签名请求,并且 [0] 显示为根 SEQUENCE 内的 SEQUENCE 内的第 4 个字段:

SEQUENCE
    SEQUENCE
        INTEGER 0
        SEQUENCE { ... }
        SEQUENCE { ... }
        [0] { ... }
    }
}

然后根据定义证书签名请求的 DER 内容的 RFC2968,定义 ASN.1 模块的附录 A,该特定字段的含义被偷偷地定义为“属性”和“应该设置 Constructed 标志”:

    attributes    [0] Attributes{{ CRIAttributes }}

您还可以通过查看根序列定义(第 4 节:“顶级类型 CertificationRequest "),找到其中的 CertificationRequestInfo 位置,并找到“属性”项在 CertificationRequestInfo 中的位置,最后查看它是如何标记的。

于 2018-01-13T00:40:14.670 回答
1

我终于解决了这个问题,并认为我可以为任何仍然试图理解这一点的人提供一些见解。在我的示例中,与上述示例一样,我使用的是 DER 格式的 X.509 证书。我遇到了“A0 03 02 01 02”序列,但无法弄清楚它是如何转换为版本号 2 的。因此,如果您遇到同样的问题,这就是它的工作原理。

A0 告诉你它是一个“上下文特定”字段,一个“构造”标签,类型值为 0x00。立即,特定于上下文的内容告诉您不要对 DER/BER 使用普通类型字段。相反,鉴于这是一个 X.509 证书,类型值在 RFC 5280, p 116 中被标记。在那里您将看到四个字段,它们的标记分别为 [0]、[1]、[2] 和 [3 ],分别代表“version”、“issuerUniqueID”、“subjectUniqueID”和“extension”。所以在这种情况下,A0 的值告诉您这是 X.509 上下文特定字段之一,特别是“版本”类型。这会处理“A0”值。

正如您所料,“03”值只是您的长度。

由于这被识别为“构造”,因此数据应表示正常的 DER/BER 对象。“02 01 02”是您要查找的实际版本号,以整数表示。“02”是整数的标准BER编码,“01”是你的长度,“02”是你的值,或者在这种情况下,你的版本号。

因此,鉴于 X.509 定义了 4 种特定于上下文的类型,您应该期望在证书中的任何位置看到“A0”、“A1”、“A2”和“A3”。希望上面提供的信息现在更有意义,并帮助您更好地理解这些标记所代表的含义。

于 2019-08-25T19:16:37.080 回答