ASN.1 如何编码对象标识符?

发布于 2024-11-05 14:50:53 字数 273 浏览 4 评论 0原文

我无法理解 ASN.1 的基本概念。

如果类型是 OID,相应的数字是否实际编码在二进制数据中?

例如在此定义中:

id-ad-ocsp         OBJECT IDENTIFIER ::= { id-ad 1 }

相应的 1.3.6.1.5.5.7.48.1 是否完全像这样编码在二进制文件中?

我问这个问题是因为我试图理解在 DER 文件(证书)中看到的特定值,即 04020500,但我不知道如何解释它。

I am having trouble understanding the basic concepts of ASN.1.

If a type is an OID, does the corresponding number get actually encoded in the binary data?

For instance in this definition:

id-ad-ocsp         OBJECT IDENTIFIER ::= { id-ad 1 }

Does the corresponding 1.3.6.1.5.5.7.48.1 get encoded in the binary exactly like this?

I am asking this because I am trying to understand a specific value I see in a DER file (a certificate), which is 04020500, and I am not sure how to interpret it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

夜未央樱花落 2024-11-12 14:50:53

是的,OID 被编码在二进制数据中。您提到的 OID 1.3.6.1.5.5.7.48.1 变为 2b 06 01 05 05 07 30 01 (前两个数字以单个字节编码,所有剩余的数字也以单个字节编码,因为它们都是小于 128)。

此处。

但分析 ASN.1 数据的最佳方法是粘贴到在线解码器中,例如 http://lapo.it /asn1js/

Yes, the OID is encoded in the binary data. The OID 1.3.6.1.5.5.7.48.1 you mention becomes 2b 06 01 05 05 07 30 01 (the first two numbers are encoded in a single byte, all remaining numbers are encoded in a single bytes as well because they're all smaller than 128).

A nice description of OID encoding is found here.

But the best way to analyze your ASN.1 data is to paste in into an online decoder, e.g. http://lapo.it/asn1js/.

美人如玉 2024-11-12 14:50:53

如果所有数字都小于或等于 127,那么它们可以用一个八位位组表示。当您有较大的常见数字时,例如 1.2.840.113549.1.1.5 (sha1WithRsaEncryption),则使用可变长度解码。这些示例侧重于解码,但编码正好相反。

1.前两个“数字”用单个字节表示

您可以通过将第一个字节读入整数进行解码

if ($firstByte >= 80) {
    $nodeFirst = 2;
    $nodeSecond = $firstByte - 80;
}
else {
    $nodeFirst = $firstByte / 40;
    $nodeSecond = $firstByte % 40;
}

$oidText = "$nodeFirst.$nodeSecond"

生成值

1.2

2。后续字节使用可变长度数量表示,也称为基数128。

VLQ 有两种形式,

短形式 - 如果八位字节以 0 开头,则使用剩余的 7 位简单地表示。

长格式 - 如果八位位组以 1(最高有效位)开头,则将该八位位组的接下来 7 位与每个后续八位位组的 7 位相结合,直到遇到最高有效位为 0 的八位位组(这标志着最后一个八位位组)。

值 840 将用以下两个字节表示,

10000110
01001000

Combine to 00001101001000 and read as int.

BER 编码的绝佳资源,http: //luca.ntop.org/Teaching/Appunti/asn1.html

第一个八位字节的值为 40 * value1 + value2。 (这一点是明确的,
因为 value1 仅限于值 0、1 和 2; value2 限制为
value1 为 0 或 1 时,范围为 0 到 39;并且,根据 X.208,n 是
始终至少为 2。)

以下八位字节(如果有)编码 value3, ...,
值。每个值均以 128 为基数进行编码,最高有效位在前,
尽可能少的数字,以及每个数字的最高有效位
值编码中最后一个字节设置为“1”。示例:
RSA Data Security, Inc. 对象的 BER 编码的第一个八位字节
标识符为 40 * 1 + 2 = 42 = 2a16。 840的编码=6*128+
4816就是86 48,113549的编码=6*1282+7716*128+d16
是 86 f7 0d。这导致以下 BER 编码:

06 06 2a 86 48 86 f7 0d


编辑/免责声明:
根据下面的评论修复了第一个八位字节,但尚未对此进行测试。我暂时保留此代码片段作为一般参考,但不能保证它是正确的,并且我不建议盲目复制和粘贴它:)。对于>128 VLQ,您通常会使用位移位来重新对齐位而不是位串。

sub getOid {
    my $bytes = shift;

    #first 2 nodes are 'special';
    use integer;
    my $firstByte = shift @$bytes;
    my $number = unpack "C", $firstByte;

    my $nodeFirst;
    my $nodeSecond;

    if ($number >= 80) {
        $nodeFirst = 2;
        $nodeSecond = $number - 80;
    }
    else {
        $nodeFirst = $number / 40;
        $nodeSecond = $number % 40;
    }


    my @oidDigits = ($nodeFirst, $nodeSecond);

    while (@$bytes) {
        my $num = convertFromVLQ($bytes);
        push @oidDigits, $num;
    }

    return join '.', @oidDigits;
}

sub convertFromVLQ {
    my $bytes = shift;

    my $firstByte = shift @$bytes;
    my $bitString = unpack "B*", $firstByte;

    my $firstBit = substr $bitString, 0, 1;
    my $remainingBits = substr $bitString, 1, 7;

    my $remainingByte = pack "B*", '0' . $remainingBits;
    my $remainingInt = unpack "C", $remainingByte;

    if ($firstBit eq '0') {
        return $remainingInt;
    }
    else {
        my $bitBuilder = $remainingBits;

        my $nextFirstBit = "1";
        while ($nextFirstBit eq "1") {
            my $nextByte = shift @$bytes;
            my $nextBits = unpack "B*", $nextByte;

            $nextFirstBit = substr $nextBits, 0, 1;
            my $nextSevenBits = substr $nextBits, 1, 7;

            $bitBuilder .= $nextSevenBits;
        }

        my $MAX_BITS = 32;
        my $missingBits = $MAX_BITS - (length $bitBuilder);
        my $padding = 0 x $missingBits;
        $bitBuilder = $padding . $bitBuilder;

        my $finalByte = pack "B*", $bitBuilder;
        my $finalNumber = unpack "N", $finalByte;
        return $finalNumber;
    }

}

If all your digits are less than or equal to 127 then you are they can be represented with a single octet each. When you have larger numbers which are common, such as 1.2.840.113549.1.1.5 (sha1WithRsaEncryption), then use it uses Variable Length Decoding. These examples focus on decoding, but encoding is just the opposite.

1. First two 'digits' are represented with a single byte

You can decode by reading the first byte into an integer

if ($firstByte >= 80) {
    $nodeFirst = 2;
    $nodeSecond = $firstByte - 80;
}
else {
    $nodeFirst = $firstByte / 40;
    $nodeSecond = $firstByte % 40;
}

$oidText = "$nodeFirst.$nodeSecond"

Produces the values

1.2

2. Subsequent bytes are represented using Variable Length Quantity, also called base 128.

VLQ has two forms,

Short Form - If the octet starts with 0, then it is simply represented using the remaining 7 bits.

Long Form - If the octet starts with a 1 (most significant bit), combine the next 7 bits of that octet plus the 7 bits of each subsequent octet until you come across an octet with a 0 as the most significant bit (this marks the last octet).

The value 840 would be represented with the following two bytes,

10000110
01001000

Combine to 00001101001000 and read as int.

Great resource for BER encoding, http://luca.ntop.org/Teaching/Appunti/asn1.html

The first octet has value 40 * value1 + value2. (This is unambiguous,
since value1 is limited to values 0, 1, and 2; value2 is limited to
the range 0 to 39 when value1 is 0 or 1; and, according to X.208, n is
always at least 2.)

The following octets, if any, encode value3, ...,
valuen. Each value is encoded base 128, most significant digit first,
with as few digits as possible, and the most significant bit of each
octet except the last in the value's encoding set to "1." Example: The
first octet of the BER encoding of RSA Data Security, Inc.'s object
identifier is 40 * 1 + 2 = 42 = 2a16. The encoding of 840 = 6 * 128 +
4816 is 86 48 and the encoding of 113549 = 6 * 1282 + 7716 * 128 + d16
is 86 f7 0d. This leads to the following BER encoding:

06 06 2a 86 48 86 f7 0d


EDIT / DISCLAIMER:
Fixed the first octet according to the comment below, but have not tested this. I'll leave this code snippet for now as a general reference, but it is not guaranteed to be correct and I do not recommend blindly copy and pasting it :). For >128 VLQ you would normally use bit-shifting to re-align the bits instead of a string of bits.

sub getOid {
    my $bytes = shift;

    #first 2 nodes are 'special';
    use integer;
    my $firstByte = shift @$bytes;
    my $number = unpack "C", $firstByte;

    my $nodeFirst;
    my $nodeSecond;

    if ($number >= 80) {
        $nodeFirst = 2;
        $nodeSecond = $number - 80;
    }
    else {
        $nodeFirst = $number / 40;
        $nodeSecond = $number % 40;
    }


    my @oidDigits = ($nodeFirst, $nodeSecond);

    while (@$bytes) {
        my $num = convertFromVLQ($bytes);
        push @oidDigits, $num;
    }

    return join '.', @oidDigits;
}

sub convertFromVLQ {
    my $bytes = shift;

    my $firstByte = shift @$bytes;
    my $bitString = unpack "B*", $firstByte;

    my $firstBit = substr $bitString, 0, 1;
    my $remainingBits = substr $bitString, 1, 7;

    my $remainingByte = pack "B*", '0' . $remainingBits;
    my $remainingInt = unpack "C", $remainingByte;

    if ($firstBit eq '0') {
        return $remainingInt;
    }
    else {
        my $bitBuilder = $remainingBits;

        my $nextFirstBit = "1";
        while ($nextFirstBit eq "1") {
            my $nextByte = shift @$bytes;
            my $nextBits = unpack "B*", $nextByte;

            $nextFirstBit = substr $nextBits, 0, 1;
            my $nextSevenBits = substr $nextBits, 1, 7;

            $bitBuilder .= $nextSevenBits;
        }

        my $MAX_BITS = 32;
        my $missingBits = $MAX_BITS - (length $bitBuilder);
        my $padding = 0 x $missingBits;
        $bitBuilder = $padding . $bitBuilder;

        my $finalByte = pack "B*", $bitBuilder;
        my $finalNumber = unpack "N", $finalByte;
        return $finalNumber;
    }

}
痴者 2024-11-12 14:50:53

OID 编码傻瓜式 :) :

  • 每个 OID 组件都被编码为一个或多个字节(八位字节)
  • OID 编码只是这些 OID 组件编码的串联
  • ,前两个组件以特殊方式编码(见下文)
  • 如果 OID 组件二进制值具有 小于 7 位,编码只是一个八位字节,保存分量值(注意,最左边的最高有效位将始终为 0),
  • 否则,如果它有 8 位或更多位,则该值是“扩展”为多个八位字节 - 将二进制表示形式分割为 7 位块(从右开始),如果需要,用零填充第一个字节,并通过添加最高有效(左)位 1(除了来自最后一个块,其中的位为 0。
  • 前两个分量 (XY) 的编码就像是值为 40*X + Y 的单个分量

这是对 ITU-T 建议 X.690,第 8.19 章的重写>

OID encoding for dummies :) :

  • each OID component is encoded to one or more bytes (octets)
  • OID encoding is just a concatenation of these OID component encodings
  • first two components are encoded in a special way (see below)
  • if OID component binary value has less than 7 bits, the encoding is just a single octet, holding the component value (note, most significant bit, leftmost, will always be 0)
  • otherwise, if it has 8 and more bits, the value is "spread" into multiple octets - split the binary representation into 7 bit chunks (from right), left-pad the first one with zeroes if needed, and form octets from these septets by adding most significant (left) bit 1, except from the last chunk, which will have bit 0 there.
  • first two components (X.Y) are encoded like it is a single component with a value 40*X + Y

This is a rewording of ITU-T recommendation X.690, chapter 8.19

雨巷深深 2024-11-12 14:50:53

这是上述内容的简单 Python 3 实现。将对象标识符的字符串形式转换为 ASN.1 DER 或 BER 形式。

def encode_variable_length_quantity(v:int) -> list:
    # Break it up in groups of 7 bits starting from the lowest significant bit
    # For all the other groups of 7 bits than lowest one, set the MSB to 1
    m = 0x00
    output = []
    while v >= 0x80:
        output.insert(0, (v & 0x7f) | m)
        v = v >> 7
        m = 0x80
    output.insert(0, v | m)
    return output

def encode_oid_string(oid_str:str) -> tuple:
    a = [int(x) for x in oid_str.split('.')]
    oid = [a[0]*40 + a[1]] # First two items are coded by a1*40+a2
    # A rest is Variable-length_quantity
    for n in a[2:]:
        oid.extend(encode_variable_length_quantity(n))
    oid.insert(0, len(oid)) # Add a Length
    oid.insert(0, 0x06) # Add a Type (0x06 for Object Identifier)
    return tuple(oid)

if __name__ == '__main__':
    oid = encode_oid_string("1.2.840.10045.3.1.7")
    print(oid)

This is a simplistic Python 3 implementation of the of above, resp. a string form of an object identifier into ASN.1 DER or BER form.

def encode_variable_length_quantity(v:int) -> list:
    # Break it up in groups of 7 bits starting from the lowest significant bit
    # For all the other groups of 7 bits than lowest one, set the MSB to 1
    m = 0x00
    output = []
    while v >= 0x80:
        output.insert(0, (v & 0x7f) | m)
        v = v >> 7
        m = 0x80
    output.insert(0, v | m)
    return output

def encode_oid_string(oid_str:str) -> tuple:
    a = [int(x) for x in oid_str.split('.')]
    oid = [a[0]*40 + a[1]] # First two items are coded by a1*40+a2
    # A rest is Variable-length_quantity
    for n in a[2:]:
        oid.extend(encode_variable_length_quantity(n))
    oid.insert(0, len(oid)) # Add a Length
    oid.insert(0, 0x06) # Add a Type (0x06 for Object Identifier)
    return tuple(oid)

if __name__ == '__main__':
    oid = encode_oid_string("1.2.840.10045.3.1.7")
    print(oid)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文