10 апреля 2012

ICEnroll::addExtensionToRequest или решение проблемы, широко известной в узких кругах

Предисловие

По долгу службы мне понадобилось генерировать запросы на сертификат (CSR - Certificate Signing Request) в браузере. Под Windows, в Internet Explorer. А чо, дело-то нехитрое: создаёшь COM-компонент (CertEnroll под Windows Vista/7/8, CEnroll под XP/2000/2003), скармливаешь ему данные, отдаёшь команду сгенерировать запрос, и voila, PKCS#10 у тебя в кармане переменной. Есть очень хорошее руководство по тому, как сделать запрос на сертификат с помощью CertEnroll - с ним, как правило, не возникает никаких проблем. Для CEnroll тоже есть руководство, и у многих в соответствии с ним всё работает, и вопросов не возникает, особенно, если достаточно запрашивать сертификаты, не содержащие расширений (X.509 extensions) специфических для какого-либо применения сертификата, идентифицирующий владельца только по DN. Чаще всего, так оно и бывает. Однако, когда возникает необходимость добавить в запрос альтернативные имена (SAR - Subject Alternative Name), многие сталкиваются с проблемой: добавляемое имя в сгенерированный запрос не попадает. Разумеется, я тоже столкнулся. При поиске в Google по названию метода addExtensionToRequest большинство ссылок окажутся жалобами на различных языках на то, что он не работает. К сожалению, среди всего этого множества обсуждений проблемы мне так и не удалось найти действительного решения. Наоборот, есть прямые указания на то, что для скриптовых языков решения нет. Но оно существует!

Проблема

Итак, типичная формулировка проблемы: есть код, который генерирует запрос на сертификат
var enroll = CreateActiveXObject("CEnroll.CEnroll");
enroll.addExtensionToRequest(0, "2.5.29.17", strExtensionBase64);
strPKCS10 = enroll.CreatePKCS10(strDN, strEKU);

* This source code was highlighted with Source Code Highlighter.
Примечание:
В strExtensionBase64 записано значение расширения, закодированное в ASN.1 / DER (бинарный формат), которое, в свою очередь, закодировано в Base64.

Данный код прекрасно выполняется, запрос генерируется, однако, вопреки ожиданиям, в сгенерированном запросе отсутствует расширение (в данном случае 2.5.29.17, добавляющее альтернативное имя).

Решение

Оказывается, метод ICEnroll4::addExtensionToRequest абсолютно ни в чём не виноват. Она действительно добавляет переданное ей значение в Enrollment Control. Виноват устаревший метод ICEnroll::CreatePKCS10, формирующий запрос PKCS#10 старой версии, в которой соответствующие расширения не предусмотрены. Вместо него следует использовать метод ICEnroll4::createRequest, указав, что хотите получить PKCS#10 версии 2.0 (Flags = XECR_PKCS10_V2_0 = 1). Таким образом, собственно вызов генерации запроса, дающий ожидаемый результат, должен выглядеть следующим образом:
strPKCS10 = enroll.createRequest(1, strDN, strEKU);
А виноваты, разумеется, разработчики MS, выбравшие названия методов, вносящие сумятицу, и не отразившие сопутствующих особенностей в документации. Ведь так логично и наглядно вызвать метод CreatePKCS10 для создания запроса PKCS#10 вместо того, чтобы вызвать createRequest и указать в параметрах, что нужно сгенерировать запрос в том же формате. Казалось бы, какая разница?

Комментариев нет: