Разбираем квалифицированные сертификаты X.509 в поисках ИНН, СНИЛС и ОГРН

в 19:33, , рубрики: .net, asn.1, C#, oid, X.509, инн, информационная безопасность, огрн, сертификат, снилс, эцп

Разбираем квалифицированные сертификаты X.509 в поисках ИНН, СНИЛС и ОГРН«Коллеги, нам необходимо вести реестр выданных квалифицированных сертификатов с возможностью поиска по ИНН, СНИЛС и ОГРН. Сколько дней нужно для создания парсера сертификатов и первого макета?» — с такого вопроса начальника началась очередная летучка.

Поскольку написанием парсера было предложено заняться мне, пришлось задуматься и покопаться в памяти, чтобы оценить трудоемкость задачи и примерные сроки на ее выполнение.

Когда-то я участвовал в небольшом проекте по моделированию SSL MITM, где отвечал за генерацию ключей и сертификатов для этого самого «человека посередине». Поэтому представлял, что квалифицированный сертификат ключа проверки электронной подписи (далее — квалифицированный сертификат) — это сертификат X.509, для описания внутренней структуры которого используется всеми любимый ASN.1.

Вот только не помнил я, чтобы тогда на глаза попадались эти ИННы, СНИЛСы и ОГРНы. Поэтому ответил более, чем скромно: «Босс, два дня, не меньше!», надеясь выполнить задачку за несколько часов.

Ниже рассказ о том, насколько сильно я ошибся в расчетах, а также готовое решение для парсинга сертификатов X.509 на C# с возможностью извлечения полей и их атрибутов с заданными объектными идентификаторами (OID).

Шаг 1. Предварительные изыскания и проверка гипотезы

Нет, серьезно, это правда, что в сертификате X.509 есть СНИЛС? Для проверки найдем образец — просим помощи у Яндекса по запросу «реестр выданных квалифицированных сертификатов», на первой же странице выдачи находим Реестр выданных УЦ ГКУ ЛО «ОЭП» сертификатов, скачиваем первый попавшийся сертификат (под номером 10842) — Komarov_Aleksey_Petrovich_2017-03-31_a2ba20c4.cer.

Комаров Алексей Петрович: взгляд изнутри

-----BEGIN CERTIFICATE-----
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 цифр. СНИЛС?

Просмотр квалифицированного сертификата с помощью стандартного средства OC Windows

Лирическое отступление

О том, что такое объектный идентификатор (OID) вообще, лучше всего почитать здесь. Как используются объектные идентификаторы в описании структуры сертификатов X.509 — смотрим Руководство по выживанию — TLS/SSL и сертификаты SSL (X.509).

Открываем сертификат в виртуальной машине с установленным криптопровайдером КриптоПРО CSP, который наверняка знает отечественную специфику, и подтверждаем догадку.

Просмотр квалифицированного сертификата с установленным криптопровайдером КриптоПро 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 и устанавливаем соответствие со структурой сертификата:

Просмотр квалифицированного сертификата в редакторе 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 г.Санкт-Петербург
ФИО физического лица / Наименование юридического лица: Комитет по природным ресурсам Ленинградской области 

Задача решена.

Подведение итогов

Итак, подытоживая проделанную работу, мы:

  1. Разобрались со структурой квалифицированных сертификатов X.509, которые попадают в сферу действия ФЗ «Об электронной подписи».
  2. Узнали, что Приказ ФСБ РФ № 795 определяет дополнительные атрибуты — ИНН, СНИЛС, ОГРН и другие. Здесь же указываются соответствующие объектные идентификаторы (OID).
  3. Разработали парсер сертификатов X.509 на C# с возможностью извлечения полей и их атрибутов с заданными объектными идентификаторами. Библиотека BouncyCastle добавила решению простоты.

В расчетах по времени произошла ошибка — вместо запланированных нескольких часов пришлось разбираться весь день. И еще два дня на подготовку исследования для Хабра.

Спасибо за внимание!

Список источников

Автор: fisher85

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js