[Из песочницы] Разбираем квалифицированные сертификаты X.509 в поисках ИНН, СНИЛС и ОГРН
Поскольку написанием парсера было предложено заняться мне, пришлось задуматься и покопаться в памяти, чтобы оценить трудоемкость задачи и примерные сроки на ее выполнение.
Когда-то я участвовал в небольшом проекте по моделированию SSL MITM, где отвечал за генерацию ключей и сертификатов для этого самого «человека посередине». Поэтому представлял, что квалифицированный сертификат ключа проверки электронной подписи (далее — квалифицированный сертификат) — это сертификат X.509, для описания внутренней структуры которого используется всеми любимый ASN.1.
Вот только не помнил я, чтобы тогда на глаза попадались эти ИННы, СНИЛСы и ОГРНы. Поэтому ответил более, чем скромно: «Босс, два дня, не меньше!», надеясь выполнить задачку за несколько часов.
Ниже рассказ о том, насколько сильно я ошибся в расчетах, а также готовое решение для парсинга сертификатов X.509 на C# с возможностью извлечения полей и их атрибутов с заданными объектными идентификаторами (OID).
Шаг 1. Предварительные изыскания и проверка гипотезы
Нет, серьезно, это правда, что в сертификате X.509 есть СНИЛС? Для проверки найдем образец — просим помощи у Яндекса по запросу «реестр выданных квалифицированных сертификатов», на первой же странице выдачи находим Реестр выданных УЦ ГКУ ЛО «ОЭП» сертификатов, скачиваем первый попавшийся сертификат (под номером 10842) — Komarov_Aleksey_Petrovich_2017–03–31_a2ba20c4.cer.
MIIIzjCCCH2gAwIBAgIRAJ6w9zrKuNKX5xH+FfdJYxwwCAYGKoUDAgIDMIH4MRww
GgYJKoZIhvcNAQkBFg11ZGNAbGVucmVnLnJ1MRgwFgYFKoUDZAESDTExMjQ3MDMw
MDAzMzMxGjAYBggqhQMDgQMBARIMMDA0NzAzMTI1OTU2MQswCQYDVQQGEwJSVTEs
MCoGA1UECAwjNzgg0LMu0KHQsNC90LrRgi3Qn9C10YLQtdGA0LHRg9GA0LMxJjAk
BgNVBAcMHdCh0LDQvdC60YIt0J/QtdGC0LXRgNCx0YPRgNCzMR0wGwYDVQQKDBTQ
k9Ca0KMg0JvQniAi0J7QrdCfIjEgMB4GA1UEAwwX0KPQpiDQk9Ca0KMg0JvQniDQ
ntCt0J8wHhcNMTcwMzMxMTAyODEwWhcNMTgwMzMxMTAyODEwWjCCAo4xIzAhBgkq
hkiG9w0BCQEWFGFwX2tvbWFyb3ZAbGVucmVnLnJ1MRowGAYIKoUDA4EDAQESDDAw
Nzg0MjM1NDk2NjEWMBQGBSqFA2QDEgswNzMxMTk5NjY2ODEYMBYGBSqFA2QBEg0x
MDc3ODQ3MTkyNjA5MSwwKgYDVQQMDCPQk9C70LDQstC90YvQuSDRgdC/0LXRhtC4
0LDQu9C40YHRgjFBMD8GA1UECww40JTQtdC/0LDRgNGC0LDQvNC10L3RgiDQu9C1
0YHQvdC+0LPQviDQutC+0LzQv9C70LXQutGB0LAxajBoBgNVBAoMYdCa0L7QvNC4
0YLQtdGCINC/0L4g0L/RgNC40YDQvtC00L3Ri9C8INGA0LXRgdGD0YDRgdCw0Lwg
0JvQtdC90LjQvdCz0YDQsNC00YHQutC+0Lkg0L7QsdC70LDRgdGC0LgxKjAoBgNV
BAkMIdGD0Lsu0KLQvtGA0LbQutC+0LLRgdC60LDRjywg0LQuNDEmMCQGA1UEBwwd
0KHQsNC90LrRgi3Qn9C10YLQtdGA0LHRg9GA0LMxLDAqBgNVBAgMIzc4INCzLtCh
0LDQvdC60YIt0J/QtdGC0LXRgNCx0YPRgNCzMQswCQYDVQQGEwJSVTEoMCYGA1UE
Kgwf0JDQu9C10LrRgdC10Lkg0J/QtdGC0YDQvtCy0LjRhzEXMBUGA1UEBAwO0JrQ
vtC80LDRgNC+0LIxajBoBgNVBAMMYdCa0L7QvNC40YLQtdGCINC/0L4g0L/RgNC4
0YDQvtC00L3Ri9C8INGA0LXRgdGD0YDRgdCw0Lwg0JvQtdC90LjQvdCz0YDQsNC0
0YHQutC+0Lkg0L7QsdC70LDRgdGC0LgwYzAcBgYqhQMCAhMwEgYHKoUDAgIkAAYH
KoUDAgIeAQNDAARA2jaQisiN++YQULAiW4b8Llik90VIKNPqUcEOVlrfplASb2W/
qc9giz+rP1/VvQXAmRKPaL3dM33MExypCJOlGaOCBEUwggRBMA4GA1UdDwEB/wQE
AwIDqDAdBgNVHQ4EFgQU7SOIwRLKC8mDQV/q2KtCaTA06Q8wNQYJKwYBBAGCNxUH
BCgwJgYeKoUDAgIyAQmDx+sNh/eeXoXlhVqDn/dWgZtags1fAgEBAgEAMIIBYwYD
VR0jBIIBWjCCAVaAFNGDmDS2EE52TJ+tKf2SJRHjAFYJoYIBKaSCASUwggEhMRow
GAYIKoUDA4EDAQESDDAwNzcxMDQ3NDM3NTEYMBYGBSqFA2QBEg0xMDQ3NzAyMDI2
NzAxMR4wHAYJKoZIhvcNAQkBFg9kaXRAbWluc3Z5YXoucnUxPDA6BgNVBAkMMzEy
NTM3NSDQsy4g0JzQvtGB0LrQstCwINGD0LsuINCi0LLQtdGA0YHQutCw0Y8g0LQu
NzEsMCoGA1UECgwj0JzQuNC90LrQvtC80YHQstGP0LfRjCDQoNC+0YHRgdC40Lgx
FTATBgNVBAcMDNCc0L7RgdC60LLQsDEcMBoGA1UECAwTNzcg0LMuINCc0L7RgdC6
0LLQsDELMAkGA1UEBhMCUlUxGzAZBgNVBAMMEtCj0KYgMSDQmNChINCT0KPQpoIR
BKgeQAWpGF6C5hHB/EETxEYwOQYDVR0lBDIwMAYIKwYBBQUHAwIGCCsGAQUFBwME
BggqhQMFARgCLAYIKoUDBQEYAgYGBiqFA2QCATBJBgkrBgEEAYI3FQoEPDA6MAoG
CCsGAQUFBwMCMAoGCCsGAQUFBwMEMAoGCCqFAwUBGAIsMAoGCCqFAwUBGAIGMAgG
BiqFA2QCATATBgNVHSAEDDAKMAgGBiqFA2RxATCCAQYGBSqFA2RwBIH8MIH5DCsi
0JrRgNC40L/RgtC+0J/RgNC+IENTUCIgKNCy0LXRgNGB0LjRjyA0LjApDCoi0JrR
gNC40L/RgtC+0J/QoNCeINCj0KYiINCy0LXRgNGB0LjQuCAyLjAMTtCh0LXRgNGC
0LjRhNC40LrQsNGCINGB0L7QvtGC0LLQtdGC0YHRgtCy0LjRjyDihJbQodCkLzEy
NC0zMDEwINC+0YIgMzAuMTIuMjAxNgxO0KHQtdGA0YLQuNGE0LjQutCw0YIg0YHQ
vtC+0YLQstC10YLRgdGC0LLQuNGPIOKEltCh0KQvMTI4LTI5ODMg0L7RgiAxOC4x
MS4yMDE2MDgGBSqFA2RvBC8MLSLQmtGA0LjQv9GC0L7Qn9GA0L4gQ1NQIiAo0LLQ
tdGA0YHQuNGPIDMuNi4xKTBWBgNVHR8ETzBNMCWgI6Ahhh9odHRwOi8vY2EubGVu
b2JsLnJ1L2UtZ292LTUuY3JsMCSgIqAghh5odHRwOi8vdWNsby5zcGIucnUvZS1n
b3YtNS5jcmwwOwYIKwYBBQUHAQEELzAtMCsGCCsGAQUFBzAChh9odHRwOi8vY2Eu
bGVub2JsLnJ1L2UtZ292LTUuY2VyMAgGBiqFAwICAwNBAEe1iKkhV4BQcDsBSAJa
kWfh5tNaPIp/jFc+HZtpmgJoGU8CpOdX3I5YAgJTx9O+Q4ylHwJl68rfCD44s0Q4
wqE=
-----END CERTIFICATE-----
Открываем сертификат с помощью стандартного средства просмотра OC Windows и находим в описании субъекта подозрительно похожую строку с объектным идентификатором 1.2.643.100.3, состоящую из 11 цифр. СНИЛС?
Лирическое отступлениеО том, что такое объектный идентификатор (OID) вообще, лучше всего почитать здесь. Как используются объектные идентификаторы в описании структуры сертификатов X.509 — смотрим Руководство по выживанию — TLS/SSL и сертификаты SSL (X.509).
Открываем сертификат в виртуальной машине с установленным криптопровайдером КриптоПРО CSP, который наверняка знает отечественную специфику, и подтверждаем догадку.
Итак, поставленная задача потенциально выполнима, СНИЛС в квалифицированном сертификате есть. Идем дальше.
Шаг 2. Знакомство с нормативной базой
Естественно, не в любом сертификате X.509 можно найти ИНН, СНИЛС и ОГРН. К примеру, если в браузере щелкнуть по замочку рядом с адресной строкой «https://yandex.ru/» и экспортировать сертификат, то там никаких длинных цифровых последовательностей не обнаружится.
При этом следует заметить, что стандарт Х.509 не ограничивает набор атрибутов, которые могут быть указаны в имени издателя и/или субъекта. Стандарт лишь рекомендует поддерживать ряд атрибутов, среди которых — страна, организация, регион, общепринятое имя и др., но о СНИЛСе и прочем не сказано ни слова.
Интересующие нас данные точно содержатся в сертификатах, которые попадают в сферу действия Федерального закона «Об электронной подписи» от 06 апреля 2011 г. № 63-ФЗ. Внимательно изучаем статью 17 и убеждаемся, что ИНН, СНИЛС и ОГРН действительно должны содержаться в квалифицированных сертификатах.
Теперь необходимо узнать, какой OID соответствует значению ИНН, а какой — СНИЛС и ОГРН. С этим вопросом поможет обзор нормативной базы, где упоминается Приказ ФСБ РФ от 27 декабря 2011 г. № 795 «Об утверждении требований к форме квалифицированного сертификата…». Открываем приказ и находим интересующую нас информацию:
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 (СНИЛС). Обращаемся к официальному реестру идентификаторов и «прогуливаемся» по дереву:
1 - International Organization for Standardization (ISO)
1.2 - ISO Member Bodies
1.2.643 - Russian federation
Значение 1.2.643.100 найти уже не удается, такой OID в списках официального каталога не значится. Переходим по ссылке в национальный реестр, продолжаем поиски. Обнаруженные идентификаторы, до которых удалось «спуститься» по дереву:
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 уточнить, каким объектам соответствуют 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 – Адрес электронной почты
Шаг 3. Разработка парсера X.509 сертификата на С#
Так сложилось, что разработка у нас ведется на 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.
На первый взгляд, можно останавливаться и несложными регулярными выражениями (пример) выбрать из строки интересующие нас значения, зная их OID«ы и типы значений. Но согласитесь, изящности такому решению явно не хватает. Кроме того, установленный криптопровайдер может заменить коды OID«ов на символьные обозначения, что приведет к лишнему усложнению кода.
Пробуем дальше, внимательно просматриваем библиотеку классов .NET в поисках подходящего решения. После нескольких попыток обратиться к полю субъекта как к последовательности байт становится понятным, что стандартными средствами платформы .NET совсем неудобно реализовывать просмотр структуры сертификата с возможностью поиска по OID«ам.
StackOverflow даёт подсказку — использовать сторонние библиотеки, выбираем наиболее цитируемую — BouncyCastle.
Библиотека подключается в один клик добавлением 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 г.Санкт-Петербург
ФИО физического лица / Наименование юридического лица: Комитет по природным ресурсам Ленинградской области
Задача решена.
Подведение итогов
Итак, подытоживая проделанную работу, мы:
- Разобрались со структурой квалифицированных сертификатов X.509, которые попадают в сферу действия ФЗ «Об электронной подписи».
- Узнали, что Приказ ФСБ РФ № 795 определяет дополнительные атрибуты — ИНН, СНИЛС, ОГРН и другие. Здесь же указываются соответствующие объектные идентификаторы (OID).
- Разработали парсер сертификатов X.509 на C# с возможностью извлечения полей и их атрибутов с заданными объектными идентификаторами. Библиотека BouncyCastle добавила решению простоты.
В расчетах по времени произошла ошибка — вместо запланированных нескольких часов пришлось разбираться весь день. И еще два дня на подготовку исследования для Хабра.
Спасибо за внимание!
Список источников
- Спецификация X.509
- Федеральный закон «Об электронной подписи» от 06 апреля 2011 г. № 63-ФЗ
- Приказ ФСБ РФ от 27 декабря 2011 г. № 795 «Об утверждении требований к форме квалифицированного сертификата ключа проверки электронной подписи»
- Официальный реестр объектных идентификаторов OID
- Национальный реестр объектных идентификаторов OID
- Руководство по выживанию — TLS/SSL и сертификаты SSL (X.509)
- ASN.1 простыми словами (кодирование типа REAL)
- Разбираем x.509 сертификат
- Электронная подпись простыми словами
- Библиотека BouncyCastle
Комментарии (1)
8 апреля 2017 в 23:42
0↑
↓
У того самого asn1 editor, которым вы пользовались для просмотра сертификата есть обычный шарповый методAsn1Parser.GetNodeByOid(string oid)
А ещё можно обойтись коробочными решениями, там есть всё, чтобы прочитать сертификат\подпись целиком.