ASN.1/DER编码整数

3
我目前开始使用DER (Distinguished Encoding Rules)编码,并且在理解整数的编码方面遇到了问题。
在参考文档https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf中,该编码定义如下:
8.3.1 整数值的编码应为原始的。内容八位组将由一个或多个八位组成。
8.3.2 如果整数值编码的内容八位组包含多个八位组,则第一个八位组和第二个八位组的第8位:
  • 不应全部为1;和

  • 不应全部为0。

  • 注意 - 这些规则确保整数值始终以最小可能数量的八位组进行编码。
    8.3.3 内容八位组应为补码二进制数,等于整数值,并由第一个八位组的位8到1,第二个八位组的位8到1,以及依次每个八位组的位8到1,直到内容八位组的最后一个八位组。
    在另一个网站https://learn.microsoft.com/en-us/windows/desktop/seccertenroll/about-integer上,解释了对于二进制以1开头的正数,需要在前面添加一个零字节。这也在stackoverflow上的一个旧问题的回答中提到:ASN Basic Encoding Rule of an integer
    不幸的是,从这些答案中我无法看出如何从参考文档的规则中推导出后一条指令。
    例如,如果我想编码数字128,为什么不能像这样做:
    [tag byte] [length byte] 10000000?
    我知道正确的编码应该是[tag byte] [length byte] 00000000 10000000,但是以上变体违反了哪个条件?可能与二进制补码有关,但是128的二进制补码难道不是10000000吗?
    我希望您能帮助我理解为什么Microsoft网站上的描述等同于原始定义。谢谢。

    也许这个链接可以帮助:https://www.strozhevsky.com/free_docs/asn1_by_simple_words.pdf - pepo
    很遗憾,到目前为止还没有。我已经考虑过这个文档,但它似乎也没有提到对于负整数会添加一个额外的零字节。相反,该文档说“负整数的编码有其自己的规则”,然后用自己的话描述了二进制补码。但在ASN.1的参考文档中,“负”这个词只出现了三次,而且没有一次是指整数的编码。所以我仍然想知道这些“自己的规则”来自哪里。 - Bilbo
    @Bilbo 它确实存在,但没有一个正常思考的人会像这样表达它。符号约束是最高有效位与符号位相遇。例如,对于正数0x80,这被违反了,您需要扩展为0x0080以满足符号约束。 - Sam Ginrich
    2个回答

    1

    二进制补码规则(8.3.3)指出,如果第一个(最低索引)内容字节的高位设置了,则该数字为负数。

    02 01 80 的内容为 0b1000_0000。由于高位被设置,因此该数字为负数。

    翻转所有位(0b0111_1111),然后加一:0b1000_0000;意味着它表示负128。

    对于一个不太极端的例子,0b1000_0001 => 0b0111_1110 => 0b0111_1111,说明0x81是负127。

    对于数字(正)127,由于高位未设置,因此该数字被解释为正数,因此其内容只是0b0111_1111,也称为0x7F,结果为02 01 7F


    抱歉,但我不认为这回答了我的问题。也许我没有表达清楚。我基本上理解二进制补码中数字的表示方式。我的问题涉及到微软网站上的一句话:“如果整数是正数,但高位比特设置为1,则在内容中添加一个前导0x00以指示该数字不是负数。” 我仍然不明白这是从哪里来的? - Bilbo
    额外的0字节意味着第一个字节的高位未设置,因此该值为正15但数字0b000_0000_1000_0000(128)。如果没有它,高位将被设置,所以数字是负数:7位值0b000_0000的二进制补码,在8位领域中为-128 - bartonjs
    1
    好的,我现在明白我错在哪里了。我以为1000 0000是128的二进制表示;但这不是作为二进制补码数的表示。如果我们考虑有符号字节,我们需要在开头加上一个0,因此我们想在开头放置一个零位。由于我们必须在编码中使用完整的字节,因此我们必须在开头放置一个完整的零字节。 - Bilbo
    @Bilbo 是的,基本上就是这样。 - bartonjs
    只是为了补充我的疑惑,微软的文章说:“如果编码的整数是正数,则TLV三元组的值字段包含该编码的整数,如果是负数,则其二进制补码。 ”这里必须指的是取二进制补码的操作(通过切换位并添加1); 显然相比之下,参考文档始终表示“内容八位组应是等于整数值的二进制补码二进制数字。” 这里指的是二进制补码的表示形式,仅针对负数切换位。 - Bilbo
    @Bilbo 二进制补码表示法意味着“如果最高有效位被设置,则这是一个二进制补码负数”,并且“如果未设置,则为正整数”。这是维基百科上的“术语潜在歧义”部分。 - bartonjs

    1
    ASN.1中的常见模式是TLV,即类型(Type) / 长度(Length) / 值(Value)。
    类型(Type):一个八位字节,0x02表示整数。
    值(Value):带符号约束的二进制补码已在上文中提到。
    长度编码有两种方式:
    1. 第一个长度字节的最高有效位未设置:那么该字节即为内容长度本身。

    2. 第一个长度字节的最高有效位设置:则紧随其后的(value-128)个八位字节形成实际长度的非负整数,字节顺序为大端序。

    长度 0 到 127 使用第一种规则,128 到更多使用第二种规则。

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接