Как ASN.1 кодирует идентификатор объекта? - PullRequest
19 голосов
/ 08 мая 2011

У меня проблемы с пониманием основных понятий ASN.1.

Если тип является OID, действительно ли соответствующее число кодируется в двоичных данных?

Например, вэто определение:

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

Кодируется ли соответствующий 1.3.6.1.5.5.7.48.1 в двоичном виде именно так?

Я спрашиваю об этом, потому что пытаюсь понятьконкретное значение, которое я вижу в файле DER (сертификат), которое 04020500, и я не уверен, как его интерпретировать.

Ответы [ 4 ]

32 голосов
/ 08 мая 2011

Да, OID кодируется в двоичных данных. Указанный вами OID 1.3.6.1.5.5.7.48.1 становится 2b 06 01 05 05 07 30 01 (первые два числа кодируются одним байтом, все остальные числа также кодируются одним байтом, поскольку все они меньше чем 128).

Хорошее описание кодировки OID можно найти здесь .

Но лучший способ проанализировать данные ASN.1 - это вставить их в онлайн-декодер, например http://lapo.it/asn1js/.

17 голосов
/ 13 июля 2014

Если все ваши цифры меньше или равны 127, то вам очень повезло, потому что они могут быть представлены одним октетом каждый. Сложнее всего, когда у вас есть общие числа, такие как 1.2.840.113549.1.1.5 (sha1WithRsaEncryption). Эти примеры фокусируются на декодировании, но кодирование - это наоборот.

1. Первые две «цифры» представлены одним байтом

Вы можете декодировать, считав первый байт в целое число

var firstByteNumber = 42;
var firstDigit = firstByteNumber / 40;
var secondDigit = firstByteNumber % 40;

Производит значения

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 * значение1 + значение2. (Это однозначно, поскольку значение1 ограничено значениями 0, 1 и 2; значение2 ограничено диапазон от 0 до 39, когда значение 1 равно 0 или 1; и, согласно X.208, n является всегда минимум 2.)

Следующие октеты, если таковые имеются, кодируют значение 3, ..., valuen. Каждое значение кодируется основанием 128, в первую очередь старшая цифра с как можно меньшим количеством цифр и наиболее значимым битом каждого Октет, кроме последнего в кодировке значения, установленного в «1». Пример: первый октет кодировки BER объекта RSA Data Security, Inc. идентификатор 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


Наконец, вот OID-декодер, который я только что написал на Perl.

sub getOid {
    my $bytes = shift;

    #first 2 nodes are 'special';
    use integer;
    my $firstByte = shift @$bytes;
    my $number = unpack "C", $firstByte;
    my $nodeFirst = $number / 40;
    my $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;
    }

}
9 голосов
/ 11 сентября 2014

Кодировка OID для манекенов :):

  • каждый компонент OID кодируется в один или несколько байтов (октетов)
  • Кодировка OID является просто объединением этих кодировок компонентов OID
  • первые два компонента кодируются особым образом (см. Ниже)
  • , если двоичное значение компонента OID имеет менее 7 бит, кодирование представляет собой всего один октет, содержащий значение компонента (примечание,самый значимый бит, крайний левый, всегда будет 0)
  • , в противном случае, если он имеет 8 и более бит, значение «разбивается» на несколько октетов - разбивает двоичное представление на 7-битные порции (справа),добавьте слева первый из них с нулями, если необходимо, и сформируйте октеты из этих септетов, добавив старший значащий (левый) бит 1, кроме последнего фрагмента, который будет иметь там бит 0.
  • первые два компонента (XY) кодируются так, как если бы это был один компонент со значением 40 * X + Y

Это переформулировка рекомендации МСЭ-Т X.690 , глава 8,19

2 голосов
/ 24 декабря 2018

Это упрощенная реализация 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)
...