Разбираем квалифицированные сертификаты 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 добавила решению простоты.

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

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

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


Поделиться публикацией

Похожие публикации

Комментарии 16

    0
    У того самого asn1 editor, которым вы пользовались для просмотра сертификата есть обычный шарповый метод
    Asn1Parser.GetNodeByOid(string oid)
    


    А ещё можно обойтись коробочными решениями, там есть всё, чтобы прочитать сертификат\подпись целиком.
      0
      Согласен, альтернативных решений может быть много. BouncyCastle выбран по понятной причине — чаще попадался на StackOverflow во время погружения в задачу, при этом простые примеры нужного нам обхода дерева после нескольких попыток так и не обнаружились.

      Одна из идей третьей части (разработка парсера) — показать, что базовыми средствами .NET не очень удобно (читай, просто и логично) «прогуливаться» по дереву в поисках нужного OID'а.
      –4
      Да уж сложно, но интересно.
        +6
        А что сложного тут вообще было?
        И цикл в парсере выглядит как-то не очень.
          +2
          Если честно, «сложного тут вообще» было немного: "… вместо запланированных нескольких часов пришлось разбираться весь день. И еще два дня на подготовку исследования для Хабра."

          Статья писалась с простой целью — сэкономить время другим читателям Хабра на тот случай, если они столкнутся с подобной задачей (и я надеюсь, поисковики начнут выдавать этот пост для тех, кто ищет «парсинг сертификатов x.509 с#», рядом с публикацией Разбираем x.509 сертификат как хорошее дополнение). Так что, позвольте считать время, потраченное на поиск решения, показателем сложности.
            +6

            Это конечно, когда уже во всем разобрался, все кажется логичным и понятным. Но когда только начинаешь погружаться в криптографию, то понимаешь, что это ЖОПА. Ведь как думаешь в начале? Да что там, ведь столько компаний вбухивают бабла в нее, такой хайп стоит, "нескольких часов хватит", чтобы разобраться.


            На деле же криптография — это какая-то секта избранных. Во-первых, очень сложно понять, с чего начать, где тот якорь, с которого начать раскрутку. Пресловутые несколько часов могут уйти только на его поиски. Во-вторых, вольные трактовки понятий. Подите с первого раза разберитесь, почему описание сертификата из RFC 5280 (структура Certificate) не соответствует тому, что лежит в файлах. Ответ — потому что там лежит структура контейнера, внутри которого и находится структура Certificate, и весь этот контейнер тоже почему-то называется сертификатом. Обычно это структура PKCS#7 и описана она в RFC 2315 (и то, еще нужно догадаться, что в файле именно она, RFC же только формат описывает. Помочь разобраться в этом могут парсеры ASN.1, о них ниже), а ведь есть еще PKCS#12 и, как вы догадываетесь, пропущенные номера также не пустуют. Разобраться во всех этих хитросплетениях без подготовки крайне тяжело. Номера RFC-шек между собой если и связаны, то явно эта связь для мозга, функционирующего отлично от человеческого.


            Пикантности ситуации добавляет тот факт, что, например в джаве, стандартным криптографическим API от Sun-овского криптопровайдера файл с сертификатом не прочитать, т.к. API требует именно структуру Certificate на входе, а вот BouncyCastle тут дает поблажку и может сам выпарсить ее из контейнера. Что на мой взгляд плохо, т.к. заставляет полагаться на неочевидное и негарантированное поведение конкретного криптопровайдера. И вот представьте, хотите вы структурировать для себя информацию, делаете демо пример, и с одним провайдером он работает, а с другим нет! И вот сиди думай, где тут ошибки — у тебя или в одной из библиотек, а если в библиотеке, то в какой именно и что гуглить.


            Потом идут поиски подходящего инструмента. Что толку, что все ссылаются на BouncyCastle? Заглянув туда в первый раз немного офигиваешь, что там за, ээ, оверинжиринг (я, правда, только его Java часть смотрел, но сомневаюсь, что в других местах лучше). Наивно думаешь, что уж в такой-то теме наверняка есть что-то более нормальное и логичное, с документацией.


            Еще пару часов рыскаешь по гуглу, проникаешься разрухой. Хорошо, если к этому моменту удастся уже выйти на RFC (еще поди пойми, которое из них сейчас актуально!) с описанием всей этой криптографической мишуры. На этом этапе уже понимаешь, что BouncyCastle, в общем то, неплох — разбор ASN.1 структур там сделан неплохо. Собственно, пожалуй, только этим и можно нормально пользоваться, даже без документации, ее вышеприведенные RFC заменяет.


            Автор в итоге и пришел к такому решению — используем хорошую часть библиотеки, забиваем на непонятную. Фактически при разборе сертификата нужно знать только его структуру, а она в RFC описана. Дальше только с помощью классов библиотеки парсим структуру и вытаскиваем нужное.


            Ну и еще хотелось бы предостеречь читателей от статьи ASN.1 простыми словами (кодирование типа REAL). На мой взгляд, там не только простые слова, а героическое возведение и штурм укреплений, когда можно было пройти через ворота. Лучше ее не читать, только каши в голове добавит.


            Да, и еще не могу не упомянуть замечательный сайт https://lapo.it/asn1js/ (у него есть и автономный режим работы — скачиваете html-ку) для декодирования ASN.1. Можно как вставлять как base64 так и hex, пробелы выкусываются автоматически

              +4

              Только вот примерно все, описанное в топике, к криптографии не имеет ни малейшего отношения. Просто есть некий файл в ASN1/DER, который надо разобрать, понять, что внутри дерево, пройтись по нему и найти там нужное. Вместо ASN1 мог быть RIFF, TIFF, EBML, BSON, Protobuf, Apache Avro и т.д. — в общем, любой бинарный контейнер. Радикально ничего от этого не меняется.

                0
                Вместо ASN1 мог быть RIFF, TIFF, EBML, BSON, Protobuf, Apache Avro и т.д. — в общем, любой бинарный контейнер. Радикально ничего от этого не меняется.

                Если подходить к вопросу с такой позиции, то вместо любого бинарного файла может быть любой другой бинарный файл — радикально от этого ничего не меняется. В конце концов, каждая программа в конечном итоге читает все файлы одинаково, пользуясь системным вызовом.


                Впрочем, соглашусь, что к академической криптографии описанное в посте действительно никак не относится. В академической криптографии вообще никаких контейнеров нет. Однако мы все же говорим о практическом применении. И форматы файлов — это тоже часть практической криптографии. Можете назвать еще хоть одну массовую, не нишевую, область, где ASN.1 используется? Сомневаюсь. Так что с практической точки зрения работа с ASN.1 почти равно криптографии. Или даже так: работа с ASN.1 почти всегда подразумевает касание криптографии.


                Вместо ASN1 мог быть RIFF, TIFF, EBML, BSON, Protobuf, Apache Avro и т.д. — в общем, любой бинарный контейнер.

                Это не совсем так. Скажем, если вы захотите сгенерировать подпись с помощью какой-то железки, которой на вход подают массив байт и ид. ключа в этой железке, а на выходе зашифрованный приватной частью этого ключа массив этих байт, то что вы будите делать? Если вы наивно полагаете, что достаточно будет запихать в нее те данные, что вы хотите подписать и получившийся массив записать в "любой бинарный контейнер", то вы заблуждаетесь. Потому, что по RFC для вычисления подписи шифруется не ваш массив, и даже не хеш от него, а хеш от ASN.1 контейнера специального вида, куда засунут хеш от вашего массива + описание алгоритма взятия хеша, параметры этого алгоритма и еще может быть чего. И шифруется уже этот контейнер, преобразованный в массив байт вполне конкретным способом.

                  +3
                  Можете назвать еще хоть одну массовую, не нишевую, область, где ASN.1 используется?

                  Я могу назвать с десяток (вроде LDAP, SNMP, H.323, UMTS, LTE, WiMAX, 3GPP и вообще очень много всего в том, что касается телекомов и вокруг них), но вы удобно скажете, что это все "нишевые области". Впрочем, чем тогда "криптография" не нишевая?


                  Или даже так: работа с ASN.1 почти всегда подразумевает касание криптографии.

                  Если мы откроем какой-нибудь более-менее современный словарь, то прочитаем там что-то вроде: "Криптография — дисциплина, включающая принципы, средства и методы преобразования информации в целях сокрытия ее содержания, предотвращения видоизменения или несанкционированного использования."


                  И вот в посте ровно пример того, где работа с ASN.1 ну совсем никак не связана с использованием криптографии. Ничего не шифруется или подписывается с целью сокрытия содержания или защиты от изменений. Сертификат используется просто как источник данных. Если бы эти данные можно было получить, обратившись в какую-нибудь БД или веб-сервис — скорее всего так бы и сделали, а не разбирали сертификаты.

                    0

                    Телеком — это именно что нишевая область. Криптография не нишевая, потому что это чисто утилитарная область, само по себе она никому не сдалась, нужна только вместе с чем-то. И областей, требующих для себя криптографии, вполне много.


                    И вот в посте ровно пример того, где работа с ASN.1 ну совсем никак не связана с использованием криптографии

                    Ну да, ничего не шифруется и не проверяется. Согласен. Однако работать с ASN.1 приходится под соусом криптографии. Даже парсер ASN.1 является частью криптографической библиотеки, а не отдельной библиотекой. Потому что там он не абстрактный парсер контейнеров с информацией, а все-таки наделяет эти контейнеры каким-то смыслом. Вот здесь у нас ContentInfo, здесь SignedData, а здесь EnvelopedData. Это уже часть криптографической библиотеки. Можно эти структуры считать "средствами… преобразования информации" из приведенного вами определения?

                      0
                      LDAP забыли ;)
            0

            Когда занимался такими же задачами, то вместо ASN1 Editor использовал asn1js. Который отлично работает со строками. В ASN1 есть несколько типов строк, и ASN1 Editor кириллицу отображает плохо. А доработать код на C++ для поддержки Unicode не так-то просто.


            И сделал пару доработок для asn1js, чтобы отображать СНИЛС, ИНН,… и иметь удобный способ сравнивать сертификаты/подписи.


            К комитам есть скриншоты, посмотрите, может пригодится:



            image

              0

              print description — классная фича, жалко, что похоже автор pull request-ы почему-то не принимает, но отослать все равно надо бы

            0

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое