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
И что же означают эти циферки-букавки? А означают они следующее:
Куда-то пропал один очень важный байт, из-за чего сервер 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.
На сегодня всё. Спокночи.
Comments: