- PVSM.RU - https://www.pvsm.ru -
«Коллеги, нам необходимо вести реестр выданных квалифицированных сертификатов с возможностью поиска по ИНН, СНИЛС и ОГРН. Сколько дней нужно для создания парсера сертификатов и первого макета?» — с такого вопроса начальника началась очередная летучка.
Поскольку написанием парсера было предложено заняться мне, пришлось задуматься и покопаться в памяти, чтобы оценить трудоемкость задачи и примерные сроки на ее выполнение.
Когда-то я участвовал в небольшом проекте по моделированию SSL MITM, где отвечал за генерацию ключей и сертификатов для этого самого «человека посередине». Поэтому представлял, что квалифицированный сертификат ключа проверки электронной подписи (далее — квалифицированный сертификат) — это сертификат X.509, для описания внутренней структуры которого используется всеми любимый ASN.1.
Вот только не помнил я, чтобы тогда на глаза попадались эти ИННы, СНИЛСы и ОГРНы. Поэтому ответил более, чем скромно: «Босс, два дня, не меньше!», надеясь выполнить задачку за несколько часов.
Ниже рассказ о том, насколько сильно я ошибся в расчетах, а также готовое решение для парсинга сертификатов X.509 на C# с возможностью извлечения полей и их атрибутов с заданными объектными идентификаторами (OID).
Нет, серьезно, это правда, что в сертификате X.509 есть СНИЛС? Для проверки найдем образец — просим помощи у Яндекса по запросу «реестр выданных квалифицированных сертификатов», на первой же странице выдачи находим Реестр выданных УЦ ГКУ ЛО «ОЭП» сертификатов [1], скачиваем первый попавшийся сертификат (под номером 10842) — Komarov_Aleksey_Petrovich_2017-03-31_a2ba20c4.cer [2].
Открываем сертификат с помощью стандартного средства просмотра OC Windows и находим в описании субъекта подозрительно похожую строку с объектным идентификатором 1.2.643.100.3, состоящую из 11 цифр. СНИЛС [3]?
Лирическое отступление
О том, что такое объектный идентификатор (OID) вообще, лучше всего почитать здесь [4]. Как используются объектные идентификаторы в описании структуры сертификатов X.509 — смотрим Руководство по выживанию — TLS/SSL и сертификаты SSL (X.509) [5].
Открываем сертификат в виртуальной машине с установленным криптопровайдером КриптоПРО CSP, который наверняка знает отечественную специфику, и подтверждаем догадку.
Итак, поставленная задача потенциально выполнима, СНИЛС в квалифицированном сертификате есть. Идем дальше.
Естественно, не в любом сертификате X.509 можно найти ИНН, СНИЛС и ОГРН. К примеру, если в браузере щелкнуть по замочку рядом с адресной строкой «https://yandex.ru/» и экспортировать сертификат, то там никаких длинных цифровых последовательностей не обнаружится.
При этом следует заметить, что стандарт Х.509 не ограничивает набор атрибутов, которые могут быть указаны в имени издателя и/или субъекта. Стандарт лишь рекомендует поддерживать ряд атрибутов, среди которых — страна, организация, регион, общепринятое имя и др., но о СНИЛСе и прочем не сказано ни слова.
Интересующие нас данные точно содержатся в сертификатах, которые попадают в сферу действия Федерального закона «Об электронной подписи» [6] от 06 апреля 2011 г. № 63-ФЗ. Внимательно изучаем статью 17 и убеждаемся, что ИНН, СНИЛС и ОГРН действительно должны содержаться в квалифицированных сертификатах.
Теперь необходимо узнать, какой OID соответствует значению ИНН, а какой — СНИЛС и ОГРН. С этим вопросом поможет обзор нормативной базы [7], где упоминается Приказ ФСБ РФ от 27 декабря 2011 г. № 795 «Об утверждении требований к форме квалифицированного сертификата...» [8]. Открываем приказ и находим интересующую нас информацию:
18. К дополнительным атрибутам имени, необходимость использования которых устанавливается в соответствии с Федеральным законом, относятся:
1) OGRN (ОГРН).
Значением атрибута OGRN является строка, состоящая из 13 цифр и представляющая ОГРН владельца квалифицированного сертификата — юридического лица. Объектный идентификатор типа атрибута OGRN имеет вид 1.2.643.100.1, тип атрибута OGRN описывается следующим образом: OGRN ::= NUMERIC STRING SIZE 13;2) SNILS (СНИЛС).
Значением атрибута SNILS является строка, состоящая из 11 цифр и представляющая СНИЛС владельца квалифицированного сертификата — физического лица. Объектный идентификатор типа атрибута SNILS имеет вид 1.2.643.100.3, тип атрибута SNILS описывается следующим образом: SNILS ::= NUMERIC STRING SIZE 11;3) INN (ИНН).
Значением атрибута INN является строка, состоящая из 12 цифр и представляющая ИНН владельца квалифицированного сертификата. Объектный идентификатор типа атрибута INN имеет вид 1.2.643.3.131.1.1, тип атрибута INN описывается следующим образом: INN ::= NUMERIC STRING SIZE 12.
Можно попробовать проверить себя — зная OID, найти описываемый им объект. Для примера возьмем OID = 1.2.643.100.3 (СНИЛС). Обращаемся к официальному реестру [9] идентификаторов и «прогуливаемся» по дереву:
1 - International Organization for Standardization (ISO)
1.2 - ISO Member Bodies
1.2.643 - Russian federation
Значение 1.2.643.100 найти уже не удается, такой OID в списках официального каталога не значится. Переходим по ссылке в национальный реестр [10], продолжаем поиски. Обнаруженные идентификаторы, до которых удалось «спуститься» по дереву:
1.2.643.100 (часть OID’а 1.2.643.100.1, соответствующего ОГРНу, и OID’а 1.2.643.100.3, соответствующего СНИЛСу) - Дополнительные идентификаторы
1.2.643.3.131 (часть OID’а 1.2.643.3.131.1.1, соответствующего ИННу) - ФГУП ГНИВЦ ФНС РОССИИ
Проверить себя не получится, поскольку не все ярусы отображены на сайте национального реестра объектных идентификаторов. Но надежда умирает последней, пробуем отправить официальный запрос оператору национального дерева – в ОАО «Инфотекс Интернет Траст». Цитируем переписку:
– Подскажите, возможно ли на сайте oid.iitrust.ru [10] уточнить, каким объектам соответствуют OID'ы: 1.2.643.3.131.1.1, 1.2.643.100.1, 1.2.643.100.3? В поиске они не находятся, но мы предполагаем, что это ИНН, ОГРН и СНИЛС. Как можно получить подтверждение этому?
– День добрый! Указанные Вами OID утверждены приказом № 795 ФСБ России от 27.12.2011.
Круг замкнулся, считаем, что проверка проведена успешно.
Продолжаем изучать Приказ ФСБ РФ от 27 декабря 2011 г. № 795 и обращаем внимание на то, что заполнение полей и их атрибутов зависит от владельца квалифицированного сертификата — физического или юридического лица. К примеру, описание заполнения атрибута commonName (общее имя):
В качестве значения данного атрибута имени следует использовать текстовую строку, содержащую имя, фамилию и отчество (если имеется) — для физического лица, или наименование — для юридического лица. Объектный идентификатор типа атрибута commonName имеет вид 2.5.4.3.
В нашем случае (Komarov_Aleksey_Petrovich_2017-03-31_a2ba20c4.cer) значением атрибута commonName является строка «Комитет по природным ресурсам Ленинградской области», следовательно, владелец сертификата – юридическое лицо.
Для юридического лица устанавливаем следующее соответствие объектных идентификаторов (не всех, выборочно, интересных нам) типам атрибутов:
2.5.4.3 – Наименование юридического лица
2.5.4.8 - Наименование субъекта Российской Федерации
1.2.643.3.131.1.1 – ИНН
1.2.643.100.1 – ОГРН
1.2.643.100.3 – СНИЛС представителя юридического лица
1.2.840.113549.1.9.1 – Адрес электронной почты
Так сложилось, что разработка у нас ведется на C#, поэтому и пример парсера будет на C#, ничего личного и никакого холивара.
Для простоты формализуем задачу следующим образом. Дано: файл квалифицированного сертификата в системе ограничений CER (Canonical encoding rules). Требуется: разобрать (распарсить) сертификат и извлечь значения ИНН, СНИЛС и ОГРН из поля субъекта (Subject).
Для первых набросков обращаемся к возможностям пространства имен System.Security.Cryptography.X509Certificates:
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Diagnostics;
namespace X509Parser
{
class Program
{
static void Main(string[] args)
{
string fileName = @"c:Komarov_Aleksey_Petrovich_2017-03-31_a2ba20c4.cer";
X509Certificate cert = new X509Certificate(File.ReadAllBytes(fileName));
Debug.Print("Серийный номер: {0}", cert.GetSerialNumberString());
Debug.Print("Дата окончания действия квалифицированного сертификата: {0}", cert.GetExpirationDateString());
Debug.Print("Субъект: {0}", cert.Subject);
}
}
}
На выходе получаем:
Серийный номер: 009EB0F73ACAB8D297E711FE15F749631C
Дата окончания действия квалифицированного сертификата: 31.03.2018 13:28:10
Субъект: CN=Комитет по природным ресурсам Ленинградской области, SN=Комаров, G=Алексей Петрович, C=RU, S=78 г.Санкт-Петербург, L=Санкт-Петербург, STREET="ул.Торжковская, д.4", O=Комитет по природным ресурсам Ленинградской области, OU=Департамент лесного комплекса, T=Главный специалист, OID.1.2.643.100.1=1077847192609, OID.1.2.643.100.3=07311996668, OID.1.2.643.3.131.1.1=007842354966, E=ap_komarov@lenreg.ru
Свойство X509Certificate.Subject возвращает имя субъекта сертификата с типом string.
На первый взгляд, можно останавливаться и несложными регулярными выражениями (пример [11]) выбрать из строки интересующие нас значения, зная их OID’ы и типы значений. Но согласитесь, изящности такому решению явно не хватает. Кроме того, установленный криптопровайдер может заменить коды OID’ов на символьные обозначения, что приведет к лишнему усложнению кода.
Пробуем дальше, внимательно просматриваем библиотеку классов .NET в поисках подходящего решения. После нескольких попыток обратиться к полю субъекта как к последовательности байт становится понятным, что стандартными средствами платформы .NET совсем неудобно реализовывать просмотр структуры сертификата с возможностью поиска по OID’ам.
StackOverflow даёт подсказку [12] — использовать сторонние библиотеки, выбираем наиболее цитируемую — BouncyCastle [13].
Библиотека подключается в один клик добавлением reference в проект. Предлагаемый уровень абстракции позволяет интуитивно понятно просматривать данные в формате ASN.1. Остается только уточнить «смещение» интересующих нас значений относительно начала файла сертификата, чтобы правильно указать позицию для парсера.
Открываем сертификат в редакторе ASN.1 Editor и устанавливаем соответствие со структурой сертификата:
(0, 2254) SEQUENCE – корневая последовательность, в скобках здесь и далее (смещение, длина) (4, 2173) SEQUENCE – сертификат (8, 3) CONTEXT SPECIFIC – версия сертификата (13, 17) INTEGER – серийный номер (32, 8) SEQUENCE – идентификатор алгоритма подписи (42, 248) SEQUENCE – имя издателя (293, 30) SEQUENCE – период действия (325, 654) SEQUENCE – имя субъекта
Нас интересует поле «Имя субъекта», в котором и записываются значения ИНН, СНИЛС и ОГРН. Если внимательно посмотреть на рисунок, можно сделать следующий вывод: поле «Имя субъекта» (325, 654) SEQUENCE представляет собой последовательность (SEQUENCE) множеств (SET), состоящих из последовательностей (SEQUENCE) пар ключ/значение.
В соответствии с этой логикой реализуем парсер:
using System.Diagnostics;
using System.IO;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.X509;
namespace X509Parser
{
class Program
{
static void Main(string[] args)
{
// Разбор сертификата в соответствии с приказом ФСБ от 27.12.2011 №795
string fileName = @"c:Komarov_Aleksey_Petrovich_2017-03-31_a2ba20c4.cer";
X509CertificateParser parser = new X509CertificateParser();
X509Certificate cert = parser.ReadCertificate(File.ReadAllBytes(fileName));
Debug.Print("Серийный номер: {0}", cert.SerialNumber.ToString(16).ToUpper());
Debug.Print("Дата окончания действия квалифицированного сертификата: {0}", cert.NotAfter.ToString("dd.MM.yyyy"));
// Разбор поля subject = SEQ of SET of SEQ of {OID/value}
DerSequence subject = cert.SubjectDN.ToAsn1Object() as DerSequence;
foreach (Asn1Encodable setItem in subject)
{
DerSet subSet = setItem as DerSet;
if (subSet == null)
continue;
// Первый элемент множества SET - искомая последовательность SEQ of {OID/value}
DerSequence subSeq = subSet[0] as DerSequence;
foreach (Asn1Encodable subSeqItem in subSeq)
{
DerObjectIdentifier oid = subSeqItem as DerObjectIdentifier;
if (oid == null)
continue;
string value = subSeq[1].ToString();
if (oid.Id.Equals("2.5.4.3")) Debug.Print("ФИО физического лица / Наименование юридического лица: {0}", value);
if (oid.Id.Equals("2.5.4.8")) Debug.Print("Наименование субъекта Российской Федерации: {0}", value);
if (oid.Id.Equals("1.2.643.3.131.1.1")) Debug.Print("ИНН: {0}", value);
if (oid.Id.Equals("1.2.643.100.1")) Debug.Print("ОГРН: {0}", value);
if (oid.Id.Equals("1.2.643.100.3")) Debug.Print("СНИЛС: {0}", value);
if (oid.Id.Equals("1.2.840.113549.1.9.1")) Debug.Print("Электронная почта: {0}", value);
}
}
}
}
}
Смотрим вывод, соглашаемся:
Серийный номер: 9EB0F73ACAB8D297E711FE15F749631C
Дата окончания действия квалифицированного сертификата: 31.03.2018
Электронная почта: ap_komarov@lenreg.ru
ИНН: 007842354966
СНИЛС: 07311996668
ОГРН: 1077847192609
Наименование субъекта Российской Федерации: 78 г.Санкт-Петербург
ФИО физического лица / Наименование юридического лица: Комитет по природным ресурсам Ленинградской области
Задача решена.
Итак, подытоживая проделанную работу, мы:
В расчетах по времени произошла ошибка — вместо запланированных нескольких часов пришлось разбираться весь день. И еще два дня на подготовку исследования для Хабра.
Спасибо за внимание!
Автор: fisher85
Источник [17]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/c-2/252142
Ссылки в тексте:
[1] Реестр выданных УЦ ГКУ ЛО «ОЭП» сертификатов: http://ca.lenobl.ru/index.php/sertifikaty
[2] Komarov_Aleksey_Petrovich_2017-03-31_a2ba20c4.cer: http://ca.lenobl.ru/certs/Komarov_Aleksey_Petrovich_2017-03-31_a2ba20c4.cer
[3] СНИЛС: https://ru.wikipedia.org/wiki/%D0%A1%D1%82%D1%80%D0%B0%D1%85%D0%BE%D0%B2%D0%BE%D0%B9_%D0%BD%D0%BE%D0%BC%D0%B5%D1%80_%D0%B8%D0%BD%D0%B4%D0%B8%D0%B2%D0%B8%D0%B4%D1%83%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BB%D0%B8%D1%86%D0%B5%D0%B2%D0%BE%D0%B3%D0%BE_%D1%81%D1%87%D1%91%D1%82%D0%B0
[4] здесь: http://www.oid-info.com/faq.htm
[5] Руководство по выживанию — TLS/SSL и сертификаты SSL (X.509): http://pro-ldap.ru/tr/zytrax/tech/ssl.html
[6] «Об электронной подписи»: http://base.garant.ru/12184522/
[7] обзор нормативной базы: https://habrahabr.ru/post/139196/
[8] «Об утверждении требований к форме квалифицированного сертификата...»: http://base.garant.ru/70133464/
[9] реестру: http://oid-info.com/cgi-bin/display?tree=
[10] национальный реестр: https://oid.iitrust.ru/
[11] пример: https://regex101.com/r/loBUA4/3
[12] подсказку: http://stackoverflow.com/questions/27064336/how-to-extract-the-email-from-a-x509-certificate-in-net
[13] BouncyCastle: http://www.bouncycastle.org/csharp/index.html
[14] X.509: http://www.itu.int/rec/T-REC-X.509-200508-S
[15] ASN.1 простыми словами (кодирование типа REAL): https://habrahabr.ru/post/150757/
[16] Разбираем x.509 сертификат: https://habrahabr.ru/post/194664/
[17] Источник: https://habrahabr.ru/post/325998/?utm_source=habrahabr&utm_medium=rss&utm_campaign=sandbox
Нажмите здесь для печати.