Как ASN.1 кодирует идентификатор объекта?
Мне трудно понять основные понятия ASN.1.
Если тип OID, действительно ли соответствующее число действительно закодировано в двоичных данных?
Например, в этом определении:
id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
Соответствует ли соответствующий 1.3.6.1.5.5.7.48.1 закодированному в двоичном формате точно так же?
Я спрашиваю об этом, потому что я пытаюсь понять конкретное значение, которое я вижу в файле DER (сертификат), который равен 04020500, и я не уверен, как его интерпретировать.
Ответы
Ответ 1
Да, 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/.
Ответ 2
Если все ваши цифры меньше или равны 127, вам очень повезло, потому что они могут быть представлены одним октетом каждый. Трудная часть заключается в том, что у вас есть большие числа, которые являются общими, например 1.2.840.113549.1.1.5 (sha1WithRsaEncryption)
. В этих примерах основное внимание уделяется расшифровке, но кодирование прямо противоположно.
1. Первые два "цифры" представлены одним байтом
Вы можете декодировать, прочитав первый байт в целое число
var firstByteNumber = 42;
var firstDigit = firstByteNumber / 40;
var secondDigit = firstByteNumber % 40;
Производит значения
1.2
2. Последующие байты представлены с использованием Variable Length Quantity, также называемого базой 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;
}
}
Ответ 3
Кодирование OID для чайников:):
- каждый компонент OID кодируется в один или несколько байтов (октетов)
- Кодирование OID - это просто конкатенация этих кодировок компонентов OID
- первые два компонента кодируются особым образом (см. ниже)
- если двоичное значение компонента OID имеет менее 7 бит, кодировка - это всего лишь один октет, содержащий значение компонента (примечание, самый старший бит, самый левый, всегда будет 0)
- в противном случае, если у него есть 8 и более бит, значение "разнесено" на несколько октетов - разбивает двоичное представление на 7 битовых фрагментов (справа), левая панель - первая с нулями, если необходимо, и формы октетов из этих септетов, добавив самый значительный (левый) бит 1, за исключением последнего фрагмента, в котором будет бит 0.
- первые два компонента (X.Y) кодируются подобно одному компоненту со значением 40 * X + Y
Это переформулировка рекомендации МСЭ-Т X.690, глава 8.19