Tags 我需要一个例子来理解ASN.1中的隐式标记

Tags 我需要一个例子来理解ASN.1中的隐式标记,tags,asn.1,Tags,Asn.1,我一直在学习下面的教程 你能用一个例子来帮助我理解隐式标记吗?我发现很清楚,它还包含(小)例子,即使是很难理解的例子,它们是相当“极端”的例子。在ASN.1中可以找到使用隐式标记的更“真实”的示例。事实上,标记有两个用途:键入和命名。键入意味着它告诉en-/解码器这是什么类型的数据类型(是字符串、整数、布尔值、集合等),命名意味着如果有多个相同类型的字段,并且其中一些(或全部)是可选的,它告诉en-/解码器该值是哪个字段的 如果将ASN.1与JSON进行比较,可以看到以下JSON数据: "Im

我一直在学习下面的教程


你能用一个例子来帮助我理解隐式标记吗?

我发现很清楚,它还包含(小)例子,即使是很难理解的例子,它们是相当“极端”的例子。在ASN.1中可以找到使用隐式标记的更“真实”的示例。事实上,标记有两个用途:键入和命名。键入意味着它告诉en-/解码器这是什么类型的数据类型(是字符串、整数、布尔值、集合等),命名意味着如果有多个相同类型的字段,并且其中一些(或全部)是可选的,它告诉en-/解码器该值是哪个字段的

如果将ASN.1与JSON进行比较,可以看到以下JSON数据:

"Image": {
    "Width":  800,
    "Height": 600,
    "Title":  "View from 15th Floor"
}
您会注意到,在JSON中,每个字段总是显式命名(“Image”、“Width”、“Height”、“Title”)并显式或隐式键入(“Title”是一个字符串,因为它的值被引号包围,“Width”是一个整数,因为它没有引号,只有数字,它不是“null”、“true”或“false”,并且没有小数点)

在ASN.1中,这段数据将是:

Image ::= SEQUENCE { 
    Width  INTEGER,
    Height INTEGER,
    Title  UTF8String
 }
这将在没有任何特殊标签的情况下工作,这里只需要通用标签。不命名数据,它们只是键入数据,因此en-/decoder知道前两个值是整数,最后一个值是字符串。第一个整数是宽度,第二个整数是高度,不需要在字节流中编码,它是由它们的顺序定义的(序列有固定的顺序,集合没有。在您提到的页面上,集合正在使用)

现在按如下方式更改模式:

Image ::= SEQUENCE { 
    Width  INTEGER OPTIONAL,
    Height INTEGER OPTIONAL,
    Title  UTF8String
 }
好了,现在我们有问题了。假设接收到以下数据:

INTEGER(750), UTF8String("A funny kitten")
750是多少?宽度还是高度?可能是宽度(缺少高度)也可能是高度(缺少宽度),两者看起来都与二进制流相同。在JSON中,这一点很清楚,因为每个数据段都有名称,而在ASN.1中则没有。现在仅仅一个类型是不够的,现在我们还需要一个名称。这就是非通用标签进入游戏的地方。将其更改为:

Image ::= SEQUENCE { 
    Width  [0] INTEGER OPTIONAL,
    Height [1] INTEGER OPTIONAL,
    Title  UTF8String
 }
如果您收到以下数据:

[1]INTEGER(750), UTF8String("A funny kitten")
你知道750是高度而不是宽度(根本没有宽度)。在这里,您声明了一个新的标记(在这种情况下是特定于上下文的标记),它有两个用途:它告诉en-/解码器这是一个整数值(键入),并告诉它这是哪个整数值(命名)

但是隐式标记和显式标记之间有什么区别呢?不同之处在于,隐式标记只是命名数据,en-/解码器需要隐式地知道该名称的类型,而显式标记名称和显式地键入数据

如果标记是明确的,则数据将按以下方式发送:

[1]INTEGER(xxx), UTF8String(yyy)
[1](xxx), UTF8String(yyy)
因此,即使解码器不知道[1]表示高度,它也知道字节“xxx”将被解析/解释为整数值。显式标记的另一个重要优点是,将来可以在不更改标记的情况下更改类型。例如

Length ::= [0] INTEGER
可以改为

Length ::= [0] CHOICE { 
    integer INTEGER,
    real    REAL 
}
标记[0]仍然表示长度,但现在长度可以是整数或浮点值。由于类型是显式编码的,解码器将始终知道如何正确解码该值,因此该更改是向前和向后兼容的(至少在解码器级别,不一定在应用程序级别向后兼容)

如果标记是隐式的,则数据将按以下方式发送:

[1]INTEGER(xxx), UTF8String(yyy)
[1](xxx), UTF8String(yyy)
不知道[1]是什么的解码器将不知道“xxx”的类型,因此无法正确解析/解释该数据。与JSON不同,ASN.1中的值只是字节。因此,“xxx”可能是一个、两个、三个或四个字节,如何解码这些字节取决于它们的数据类型,而数据流本身并没有提供这些数据类型。同时,更改[1]的类型肯定会破坏现有的解码器

好吧,但是为什么会有人使用隐式标记呢?总是使用显式标记不是更好吗?使用显式标记时,还必须在数据流中对类型进行编码,这将需要每个标记额外两个字节。对于包含数千(甚至数百万)个标签的数据传输,如果每个字节都计数(连接速度非常慢、数据包很小、数据包丢失率高、处理设备非常弱),并且双方都知道所有自定义标签,为什么要浪费带宽、内存、存储和/或处理时间进行编码,发送和解码不必要的类型信息


请记住,ASN.1是一个相当古老的标准,它的目的是在网络带宽非常昂贵,处理器速度比今天慢几百倍的时候实现高度紧凑的数据表示。如果你看看今天所有的XML和JSON数据传输,甚至想到每个标记节省两个字节似乎都很可笑。

使用公认的答案作为编码示例:

Image ::= SEQUENCE { 
    Width  INTEGER,
    Height INTEGER,
    Title  UTF8String
}
编码的一个例子是:

Image ::= SEQUENCE { 
    Width  INTEGER,
    Height INTEGER,
    Title  UTF8String
 }

内部顺序分为:

显式可选 如果您有
显式可选值

Image ::= SEQUENCE { 
    Width  [0] EXPLICIT INTEGER OPTIONAL,
    Height [1] EXPLICIT INTEGER OPTIONAL,
    Title  UTF8String
}
编码序列可能是:

  • 序列
    30 15 A1 02 02 02 EE 0C 0E 41 20 66 75 6E 6E 79 20 6B 69 74 74 65 6E
    (21字节)
而内部顺序又分为:

  • 上下文[1]整数
    A1
    02
    02
    02 EE
    750(2字节)
  • UTF8STRING
    0C
    0E
    412066756e6e79206b697474656e
    “一只有趣的小猫”(14字节)

我在示例中迷失了方向参考:。这里,a)a::=值为5的整数编码为十六进制02 01 05,b)b:=[2]值为5的隐式整数编码为十六进制82 01 05,c)c:=[2]值为5的显式整数编码为十六进制A2 03 02 01 05。有人能帮我吗