Contents of this directory is archived and no longer updated.

MSDN и Microsoft меня однажды доканает и я уйду проповедовать православныйбогомерзкий линукс.

А дело в том, что одна моя задумка с треском проваливалась из-за непонятного и тупого бага в интерфейсе ICertEncodeAltName. Это CryptoAPI интерфейс, который позволяет кодировать строковые значения для расширения Subject Alternative Name в ASN.1 DER encoded строку. На возню с ним я потратил почти неделю (чуть меньше, наверное) и только сегодня я смог найти все ответы. Хотя я это должен был сделать много раньше, но дико тупил на ровном месте. Итак, давайте посмотрим, что из себя представляет этот баг.

Для начала я покажу как собирается этот интерфейс:

$san = New-Object -ComObject CertificateAuthority.EncodeAltName

Далее, его надо инициализировать методом Reset() и в качестве аргумента метода указать размер объекта. Т.е. количество элементов, которые должны содержаться в расширении Subject Alternative Name. Если только один элемент, то и указываете число 1:

$san.Reset(1)

и теперь можно в него заряжать данные методом SetNameEntry():

$san.SetNameEntry(0,0x3,"Custom-Name")

Где 0 — индекс массива, в который надо записать строку, 0x3 — тип строки. В данном случае это DNS и последний аргумент указывает само значение строки. Полный список типов можно получить вот здесь: http://msdn.microsoft.com/en-us/library/aa374981(VS.85).aspx. Только учтите, что реально типы начинаются не с нуля (как по ссылке), а с единицы. т.е. 0x3 в нашем случае это DNS (2+1).

После того как мы загнали туда данные, их можно кодировать:

$san.Encode()

И давайте посмотрим, что у нас получилось:

[↓] [vPodans] $san = New-Object -ComObject CertificateAuthority.EncodeAltName
[↓] [vPodans] $san.Reset(1)
[↓] [vPodans] $san.SetNameEntry(0,0x3,"Custom-Name")
[↓] [vPodans] $str = $san.Encode()
[↓] [vPodans] $str
???????

выглядит прикольно. Но на самом деле это строка из символов, которые описаны не одним, а двумя байтами:

[↓] [vPodans] $str.ToCharArray() | %{[int][char]$_}
3376
2946
30019
29811
28015
20013
28001

Чтобы убедиться, что в каждом символе по 2 байта, мы преобразуем эти числа в их шестнадцатиричное (hex) представление:

[↓] [vPodans] $str.ToCharArray() | %{"{0:X4}" -f [int][char]$_}
0D30
0B82
7543
7473
6D6F
4E2D
6D61

видите, в каждом символе по 2 байта и все они записаны в little-endian последовательности. Т.е. первый байт находится справа, а не слева, как обычно. Давайте соберём эти байты в последовательную строку, переставив байты местами в каждой строке:

30 0D 82 0B 43 75 73 74 6F 6D 2D 4E 61 6D

И что же означают эти циферки-букавки? А означают они следующее:

  • 30 (48) — тип кодировки. 30 или 48 (в десятичном представлении) означает Constructed encoding.
  • 0D (13) — длину строки начиная со следующего байта. Остаточная длина должна быть 13 байт. Но моя математика говорит, что дальше не 13, а только 12 байт.
  • 82 (130) — тип закодированной строки. 130 означает DNS. 80 (128) — OtherName, 81 (129) — RFC822Name и т.д.
  • 0B (11) — размер строки начиная со следующего байта. Остаточная длина должна быть 11 байт. Но моя математика говорит, что их там не 11, а только 10 байт.
  • Остальные байты — сама строка, только в hex'е.

Куда-то пропал один очень важный байт, из-за чего сервер CA начинал доставлять шлакоблоки при виде такой строки. В принципе, мы можем посмотреть, что у нас потерялось по дороге:

[↓] [vPodans] $str1 = "43 75 73 74 6F 6D 2D 4E 61 6D".split(" ")
[↓] [vPodans] $str1 | %{$a += invoke-expression [char]0x$_}
[↓] [vPodans] $a
Custom-Nam

Последний символ куда-то провалился. Нет его больше. Т.е. размер вычислен правильно, просто не хватает одного байта. Причём, если попробовать что-то другое, попроще, интерфейс возвращает правильный размер и все байты. Я не знаю точно, в каких условиях этот баг проявляется, но уже очевидно, что если в строке присутствует дефис, уже начинается бяка.

Что можно сделать? Вариантов куча. Как вы видите ASN.1 DER кодировка достаточно простая, её можно сделать самому. Либо использовать православные интерфейсы CertEnroll — IX509ExtensionAlternativeNames. На первый взгляд там багов не обнаружено :-). Примеры использования данных интерфейсов можно посмотреть (а заодно и почитать) в моём буржуйском бложике: How to add FQDN to HP iLO request.

На сегодня всё. Спокночи.


Share this article:

Comments:

Comments are closed.