Что в tls 1.3?
Все упомянутые трудности решаются использованием TLS 1.3. Половины проблем вообще нет, всё проще, красивее, всё прям отличненько. Но TLS 1.3 еще распространен маловато. Самые важные отличия TLS 1.3 (их очень много, они везде, поэтому только самые важные):
- Плохие шифры удалены, остались только хорошие. Инициализировать сессию TLS 1.3 на плохих шифрах не получится. Всё дело в новых cipher suites: тут и другие алгоритмы обмена сеансовых ключей, и так называемый HMAC – не будем углубляться в подробности, потому что Патрик устал. Вкратце: раньше подпись каждого TLS-пакета шла отдельно. На это были атаки, потому что было известно, какой там контент находится. Сейчас ее запихали вовнутрь (режим AEAD), и в TLS 1.3 по-другому быть не может, соответственно, мы избавились от таких атак.
- Handshake стал короче – нет старых сообщений, нет старых расширений, нет возможности по каким-то странным штукам обменяться ключиками. То есть, он тупо короче количественно – даже самый полный TLS 1.3 handshake короче, чем в TLS 1.2.
- Переход на шифрованный канал происходит почти что сразу. Для этого используются разные ключики: да, пока не договорились о хороших ключиках, оптимальных, мы используем какие попало, но канал уже шифрованный. То есть hello – hello и пошло всё зашифрованное. Из-за этого сложнее всё это ломать.
- Всё регламентировано, больше не надо пытаться менять размеры пакетов, забивая их ноликами, чтобы сложнее было расшифровывать.
- Своя пара ключей на каждую сеансовую фазу. Сеансовые ключи меняются: пока мы ни о чем не договорились – они такие, договорились о более крутых – они более крутые, сертификаты проверили и всё хорошо – еще другие ключи. В итоге их много, они усложняются и очень трудно это всё поломать. Еще одна важная вещь, почему это быстрее: Early Data (она же 0-RTT, Zero Round Trip Time) – это когда у тебя в TLS-handshake посылается полезная инфа – ну например GET-запрос. То есть нет такого, что поговорили-поговорили и только потом посылаем что-то отдельным потоком. Сразу же в handshake идет запрос. Как только сервер его получил, он начинает его обрабатывать, отдает клиенту свои данные, сертификат и пока клиент проверяет, сервер уже готов отдать. И может даже в TLS-handshake и отдать иногда.
- Есть pre-shared key, то есть клиент с сервером могут договориться и сохранить сеансовые ключи для последующих соединений. И, соответственно, когда происходит handshake таких вот договорившихся клиентов, на этап выбора ключей время не тратится. Долго объяснять, как это сделано криптографически, но тех атак, которые были на Session ID, вот в этом месте сейчас нет (что хорошо). Всё стало безопаснее и быстрее.
Основная проблема, что поддержка TLS 1.3 – она во всех браузерах, которые актуальны, есть, но не во всех по дефолту включена. Например, в Safari нет (но там очень легко включить), Google Chrome и Mozilla Firefox уже по дефолту поддерживают TLS 1.3. Ngnix с TLS 1.3 – без проблем, в Apache есть нюансы, а вот с почтовыми клиентами хуже — там только Exim молодец, а остальные не очень.
В общем, это наше будущее, там всё получше и попроще, самое главное. Но пока оно еще не везде наступило.
Capi и scvp
Еще про верификацию и про цепочку. Есть маленькая особенность – похвалим здесь Windows. Если сервер не отдал сертификат, в обычном (традиционном) подходе сертификат нам взять неоткуда, цепочки нет, всё сломалось. Так вот, в Windows есть такая штука как Certificate API, и она может достроить цепочку, взяв промежуточные сертификаты из своего хранилища.
Как эти сертификаты туда попадают? Либо залиты в хранилище, либо из сертификата, установленного на сервере. Например, в Plesk, если получить сертификат от Comodo и поставить его на домен – в хранилище попадет промежуточный сертификат от Comodo.
Ну и еще люди, которым Windows нравится, придумали SCVP (Server-based Certificate Validation Protocol). В действительности не работает почти ни у кого и нигде – в смысле глобально и массово, — но как концепция есть. Более того, есть продукты, которые это делают, и даже в каких-то сетях это может быть настроено.
Это сервис, который за тебя эту цепочку строит и частично даже проверяет, что удобно. Если там заявлена поддержка DPV (Delegated Path Validation), то он цепочку еще и провалидирует. То есть, клиенту надо этому сервису отправить сертификат и получить ответ – продолжать сессию или рвать.
Предположительно это могло бы всё ускорить, но из-за того, что сервису тоже надо как-то доверять, идея глохнет. И всё-таки она была.
Итак, вот у нас получается такая вот цепочка.
Прежде всего мы проверяем, что с подписями у нас всё нормально: выстраиваем цепочку, проверяем подписи – и считаем, что содержимое сертификатов верно. Дальше нам надо проверить конечный сертификат. Промежуточный нам проверять не надо, потому что мы идем к Патрику и проверить нам надо только самый последний сертификат.
Давайте проверять.
Former blog – pki extensions
Предлагаю немного отвлечься от autoenrollment и поговорить о часто задаваемым вопросам, которые относятся к проблемам certificate chaining engine. Данный FAQ основывается на вопросах, которые часто задаются на форумах TechNet.
Q: За что отвечают расширения CRL Distribution Points (CDP) и Authority Information Access (AIA) в цифровых сертификатах?
A: Расширение Authority Information Access всегда содержит ссылки на сертификат Certification Authority (CA), который издал рассматриваемый сертификат. Например, у вас есть сервер CA, который издаёт пользователям сертификаты. Каждый такой сертификат будет содержать ссылку на скачивание сертификата этого самого CA. Это необходимо для построения цепочки сертификатов. Как известно, любая иерархия PKI включает себя как минимум один корневой CA (Root CA) и может содержать несколько уровней промежуточных CA. Используя расширение AIA конечного сертификата, клиент скачивает сертификат издающего CA, который в расширении AIA так же содержит ссылки на скачивание сертификата вышестоящего CA. Этот процесс происходит до тех пор, пока цепочка не закончится самоподписанным сертификатом и который уже не содержит расширения AIA. Поскольку корневой сертификат самоподписанный, он является конечной точкой цепочки. По корневому сертификату определяется доверие цепочке. Чтобы доверять такой цепочке, корневой сертификат, на котором эта цепочка заканчивается, должен быть установлен в контейнере Trusted Root CAs в оснастке Certificates консоли MMC.
Расширение CRL Distribution Point содержит ссылки на файлы CRL, которые периодически публикует сервер CA, который издал рассматриваемый сертификат. Этот файл содержит серийные номера и прочую информацию о сертификатах, которые были отозваны. Сертификаты могут быть отозваны, когда есть подозрения или уже констатирован факт получения несанкционированного доступа к закрытому ключу третьим лицом. Закрытый ключ должен быть доступен только тому пользователю или компьютеру, которому выдан данный сертификат. Если третье лицо получило доступ к закрытому ключу, это лицо может воспользоваться им для получения конфиденциальной информации или выдавать себя за легитимного пользователя. В таких случаях администратор CA может отозвать такой сертификат. В таком случае если сертификат содержится в файле CRL, предъявителю данного сертификата никакого доверия не будет. Следует понимать, что CDP/AIA рассматриваемого сертификата содержат ссылки на файлы вышестоящего CA. Даже если владельцем рассматриваемого сертификата является сервер CA (Intermediate CA), ссылки будут указывать на файлы вышестоящего CA.
Более подробно вопрос рассмотрен по ссылке: Certificate Chaining Engine — как это работает.
Q: Нужны ли расширения CDP и AIA в сертификатах корневых CA?
A: Нет. Как уже было сказано, CDP и AIA содержат ссылки на файлы вышестоящего CA, а корневой сертификат является высшей точкой в иерархии и выше корневого CA ничего быть не может. Поэтому данные расширения бесполезны для сертификатов корневых CA и обычно просто отсутствуют.
Более подробно вопрос рассмотрен здесь: Certificate chaining engine (CCE) и отзыв корневых сертификатов
Q: На каком сервере CA я могу отозвать определённый сертификат?
A: каждый сервер CA поддерживает свой список CRL в который может включать только те сертификаты, которые были выданы именно этим CA. Это означает, что сертификат отозвать может только CA, который указан в поле Issuer рассматриваемого сертификата.
Q: Стоит ли использовать Certificate Hold в качестве причины отзыва сертификата? Если да, то в каких случаях?
A: Причина отзыва как Certificate Hold обладает одной особенностью — его можно извлечь обратно из CRL файла, после чего он перестанет считаться отозванным. Данная причина задумана для случаев когда сотрудник уходит в отпуск и такой отзыв позволит избежать использование отозванного сертификата на этот период. Однако, данную причину не следует никогда использовать. Это связано с тем, что после извлечения сертификата из CRL вы не можете узнать был ли он отозван в какой-то период времени. Например, вы отозвали сертификат с причиной Certificate Hold и через некоторое время передумали и извлекли сертификат из CRL. В последствии вы получаете документ, подписанный этим сертификатом именно в период фактического нахождения сертификата в CRL. Фактически получается, что документ был подписан в тот момент, когда сертификат был отозван. Поскольку он был потом извлечён из CRL, вы больше не можете доказать, что сертификат был отозван на момент подписания документа. По этой причине вы не должны использовать данную причину отзыва.
Q: При установке сертификата на сервер иногда получаю ошибку: The revocation function was unable to check revocation because the revocation server was offline. 0x80092021 (-2146885613). Что это может означать и как эту ошибку исправить?
A: Данная ошибка указывает на то, что системе не удалось проверить сертификат на отзыв. Иными словами, система извлекла ссылку из CDP расширения сертификата и по этой ссылке не удалось скачать CRL файл. В данном случае вы должны обратиться к администратору сервера CA, чтобы тот обеспечил доступность файла по ссылкам в CDP расширении. Если же вы сами исполняете эти обязанности, то вы должны самостоятельно решить этот вопрос — настроить точки публикации CRL файлов. В качестве временного решения (например, вы опубликовали CRL только в Active Directory и у вас несколько доменов в лесу, причиной этого может быть репликация AD. В таком случае вы можете скачать CRL файл локально на компьютер (любым удобным способом) и выполнить команду: certutil –addstore –f Root <pathfile.crl>
где <pathfile.crl> — путь к файлу CRL.
Q: При открытии файла сертификата я вижу следующее сообщение: Windows does not have enough information to verify this certificate. Что это означает и как с этим бороться?
A: Данное сообщение говорит о том, что системе не удалось найти сертификат вышестоящего CA. Как уже было отмечено, этот сертификат можно скачать по ссылке (ссылкам) в расширении AIA, но по всей видимости они нерабочие. Вы можете попробовать вручную скачать файл по этим ссылкам и вероятней всего вам это не удастся. Для решения этой проблемы вы должны опубликовать физический файл в соответствующие места: папка веб-сервера (для протокола HTTP) или контейнер AIA в Active Directory (для протокола LDAP). Если файл сертификата CA публикуется только в AD и у вас лес с несколькими доменами, данная ошибка может быть кратковременно вызвана задержками репликации. В таком случае для временного решения вы можете скачать файл на локальную машину (любым удобным для вас способом) и выполнить одну из двух команд:
certutil –addstore –f SubCA <pathfile.crt> — если вышестоящий CA не является корневым
certutil –addstore –f Root <pathfile.crt> — если вышестоящий CA является корневым.
где <pathfile.crt> — путь к файлу CER/CRT.
Q: Я отозвал сертификат, опубликовал новый CRL, однако при запуске файла сертификата я не вижу ошибок, что сертификат отозван. Почему?
A: Когда вы запускаете файл сертификата (просматриваете его содержимое), проверка на отзыв не происходит. Система только строит цепочку, но ни один сертификат на отзыв не проверяется. Это by design. Для проверки статуса сертификата вы можете воспользоваться следующими командами:
certutil –url <pathfile.cer> — этой командой вызывается удобный графический интерфейс, в котором вы можете выяснить статус отзыва сертификата.
certutil –verify –urlfetch <pathfile.cer> — данная команда выведет очень подробный текстовый лог проверки. Рекомендуется для использования профессионалами, которые в состоянии проанализировать этот лог.
где <pathfile.cer> — путь к файлу проверяемого сертификата.
Q: Я отозвал сертификат, опубликовал новые CRL, однако certutil показывает, что сертификат не отозван. Как так?
A: Это вызвано тем, что CRL’ы, как и ответы от OCSP Responder кешируются в оперативной памяти и жёстком диске. Для обновления кеша в памяти, следует перезапустить приложение, которое проверяет статус сертификата. А на самом диске CRL кешируется на срок действия конкретного CRL. Это значит, что клиент будет использовать данный кешированный CRL до срока указанного в поле Next update конкретного CRL файла. Кеширование используется для экономии ресурсов и пропускной способности сети. Но вы можете очистить кеш CRL (при этом удалятся все кешированные CRL), вы можете воспользоваться следующей командой:
certutil -setreg chainChainCacheResyncFiletime @now
после этого обязательно следует выполнить следующую команду:
certutil –delreg chainChainCacheResyncFiletime
Данные команды поддерживаются системами начиная с Windows XP SP3. Предыдущие системы не поддерживают очистку кеша CRL.
Q: Какие протоколы разрешены для расширений CDP/AIA?
A: Для Windows систем доступны только следующие протоколы:
HTTP:// — только для публикации в расширение сертификата.
LDAP:// — для публикации физических файлов и для публикации ссылки в расширение сертификата.
FILE:// — только для публикации физических файлов в сетевой папке (например, на веб-сервере). Хотя Windows Server 2003 и предыдущие системы позволяли такие ссылки в расширениях CDP/AIA, начиная с Windows Vista/Windows Server 2008 они больше не допускаются и не используются.
C:path — только для публикации физических файлов в файловой системе локального компьютера.
Более подробно можно прочитать по ссылке: CRL Distribution Points и Authority Information Access
Q: есть ли какие-то best practices по планированию расширений CDP/AIA?
A: да, существют определённые best practices по настройке расширений CDP/AIA на сервере CA. И вот как они выглядят в общем виде:
1) не следует использовать ссылки вида LDAP:// если ваши сертификаты могут использоваться вне пределах вашего леса AD. Это вызвано тем, что скачивать файлы по таким ссылкам могут только клиенты текущего леса, остальные клиенты будут получать ошибки по этим ссылкам. Целесообразно использовать более универсальные протоколы как HTTP. Данный протокол поддерживается множеством систем, в том числе и мобильными системами (смартфоны) и пользователями других операционных систем. При использовании HTTP рассмотрите возможность высокой доступности этих веб-серверов, как NLB-кластер или использования двух различных веб-серверов. В таком случае вы должны будете указать ссылки на скачивание файла для каждого веб-сервера.
2) используйте веб-сервера, которые доступны как изнутри сети, так и из интернета (если сертификаты могут использоваться пользователями интернета, например SSL-сертификаты) по одному и тому же URL.
3) при планировании ссылок, которые будут публиковаться в расширениях CDP/AIA сертификатов следует учитывать их очерёдность. Если вы решили использовать 2 независимых веб-сервера, первым указывайте тот, который будет являться основным. Это обусловлено тем, что клиент скачивает файлы по ссылкам в порядке их следования. Первая ссылка будет использоваться в первую очередь. Для ссылок публикации физического файла очерёдность не имеет значения.
4) для HTTP ссылок никогда не используйте SSL (т.е. HTTPS ссылки), поскольку это вызовет бесконечную проверку сертификатов, в результате чего вы никогда не скачаете нужный файл.
Более подробно можно прочитать по ссылкам: CRL Distribution Points и Authority Information Access, OCSP или CRL?
Q: Какие версии ОС Windows поддерживают нативного клиента OCSP?
A: Только ОС начиная с Windows Vista/Windows Server 2008 включают в себя клиента OCSP. Предыдущие системы никогда не поддерживали и не будут поддерживать клиента OCSP. Для них придётся приобретать отдельный программный продукт сторонних компаний.
Q: Я хочу использовать OCSP Responder как для внутренней, так и для внешней сети (интернета). На какое имя должен выдаваться сертификат OCSPResponseSigning?
A: на самом деле клиенту всё равно на какое имя выдаётся данный сертификат. Главное — сертификат должен отвечать следующим условиям:
1) должен быть выдан тем же CA для которого OCSP Responder осуществлял проверку статуса сертификата.
2) в Application Policies (Extended Key Usage) должен быть указан: OCSP Signing
3) в сертификате должно присутствовать расширение: OCSP No Revocation Checking
4) срок действия сертификата должен быть действующий.
Q: Мы в компании перешли на Windows Vista/Windows 7 и развернули OCSP Responder. Однако в сети уже выпущено огромное количество сертификатов, которые не содержат ссылок на OCSP Responder. Надо ли перевыпускать все сертификаты для включения в них ссылки на OCSP?
A: нет, вам не нужно перевыпускать все сертификаты, которые уже используются. Вы можете для доменных клиентов настроить групповую политику так, чтобы они получили адрес вашего OCSP Responder и которым они будут пользоваться при проверке сертификатов. В качестве руководства по настройке этой политики можете воспользоваться вот этой ссылкой: Appendix A: Managing OCSP Settings with Group Policy
Что бы почитать?
§
Этим постом я начинаю серию небольших заметок по не всегда очевидным фактам в PKI. И сегодня начну с первого факта — OCSP или CRL? OCSP и CRL — это 2 модели, которые могут использоваться для проверки сертификата на предмет его отзыва.
Сертификаты в случае когда в них больше не нуждаются или когда закрытый ключ от сертификата может быть доступен злоумышленникам подлежат отзыву. Этим самым полностью теряется доверие данному сертификату или человеку, который его предъявил, поскольку сертификат может предъявить злоумышленник, который его украл. Это достаточно важный момент в PKI, который позволяет достаточно однозначно сказать — можем ли мы доверять предъявителю конкретного сертификата или нет.
Модель CRL используется с самого начала существования PKI и выглядит следующим образом. Сервер CA отзывает некоторый сертификат и помещает о нём запись в специальный файл CRL. При этом туда помещается серийный номер сертификата, дата фактического отзыва (не обязательно совпадает со временем когда администратор отозвал сертификат) и причина отзыва. Клиенты при проверки сертификатов скачивают эти CRL и проверяют есть ли серийный номер этого сертификата в CRL. Логично, что чем больше сертификатов отозвано, тем больше файл, тем более нагружается сеть при частом скачивании относительно большого файла CRL.
В OCSP используется немного иной принцип. Клиент OCSP отправляет на сервер OCSP Responder специальный запрос, в котором содержится серийный номер проверяемого сертификата. OCSP Responder «пробивает номер по своей базе» и отвечает клиенту откликом. Этот отклик содержит однозначный ответ на вопрос: отозван или не отозван.
Казалось бы, если у вас и серверы и клиенты работают под управлением Windows Vista/2008 и выше, то ответ однозначный: OCSP — наше всё! Ну и вообще даже при наличии небольшого количества этих систем (а преимущественно используются устаревшие клиенты Windows XP/2003), то OCSP нам никак не помешает! Но на самом деле это не всегда так и в ряде ситуаций использование OCSP будет даже нежелательным. Давайте посмотрим почему. Сначала сделаем сравнительные характеристики обеих моделей проверки отзыва
Исходный размер пустого CRL составляет порядка 600 байт (но это значение может отличаться в зависимости от длины полей и длины ключа подписи). На каждую запись отозванного сертификата в CRL отводится 80 байт. Это, как я уже говорил, серийный номер отозванного сертификата, дата фактического отзыва и числовое обозначение причины отзыва. Т.е. если у вас отозвано 10 сертификатов, то размер CRL будет 600 80 * 10 = 1400 байт. Если отозвано 100 сертификатов, то размер CRL будет порядка 9кб и т.д.
Каждая проверка статуса сертификата потребляет определённый размер трафика — 2кб. При этом неважно какого размера сам CRL. Даже если отозвано 100 000 сертификатов, то CRL будет размером примерно 8мб. И каждый устаревший клиент будет скачивать этот файл на 8 мегабайт. А с OCSP любой запрос и ответ на него займёт всего 2кб. Удобно? Безусловно.
Однако, в ряде случаев я бы не рекомендовал использовать OCSP для сертификатов, которые аутентифицируют клиентов. К ним относятся следующие типы сертификатов:
- сертификаты смарт-карт;
- сертификаты аутентификации в IIS;
- клиентские сертификаты компьютеров для VPN/L2TP и IPsec;
- сертификаты аутентификации пользователей в VPN (EAP-TLS).
Почему? Дело в том, что аутентифицирующий сервер кеширует все данные об отзыве. Сначала в памяти, потом на диске (когда происходит свопинг). И давайте посмотрим на типичный сценарий:
У вас на предприятии используется корпоративный веб-сайт с аутентификацией пользователей по сертификату. У вас 1000 пользователей и они все утром подключаются к веб-сайту. Веб-сервер при проверке подлинности каждого пользователя будет посылать OCSP запрос на OCSP Responder запросы с серийным номером каждого сертификата. Мы знаем, что размер каждого ответа составляет 2кб, следовательно сервер отправит 1000 HTTP запросов и закеширует у себя в памяти 2 мегабайта памяти.
А теперь представьте, что у вас не используется OCSP, а только CRL и на сервере CA отозвано 100 сертификатов. Как мы знаем сам контейнер CRL занимает 600 байт и каждая запись по 80 байт. В итоге файл CRL будет занимать 9 килобайт! И в этом случае сервер сделает только одну «ходку» за CRL файлом и уже всех клиентов будет проверять именно по скачанному CRL. Как видите, профит от использования CRL здесь очевиден. Серверу надо меньше тратить сетевого трафика и меньше данных кешируется в памяти. При этом OCSP целесообразно включать для серверных сертификатов, потому что клиентов обычно много и им выгодней использовать небольшии порции трафика OCSP.
Подводя итоги, мы можем кратко констатировать следующие вещи:
- для серверных сертификатов рационально использовать OCSP;
- для клиентских сертификатов, которые используются для аутентификации часто рациональней будет использовать классические CRL, вместо OCSP.
Поэтому при рассмотрении вопроса внедрения OCSP вы должны учитывать эти моменты, чтобы ваша PKI была более эффективной.
Ссылки по теме:
OCSP (часть 1)
OCSP (часть 2)
http://en.wikipedia.org/wiki/Certificate_revocation_list
Intermediate certificate
Следующий в цепочке – Intermediate Certificate. Зачем он нужен? Дело в том, что в случае, если с Root CA Certificate что-то пошло не так, очень сложно его поменять. Центру сертификации нужно сделать новый приватный ключ, новый публичный ключ, всем это всё нужно обновить и так далее, это утомительно и это ломает всю секьюрити.
И вообще, чем меньше мы работаем с машиной, на которой находится этот CA, тем меньше шансов, что этот приватный ключ украдут. Поэтому CA выписывают промежуточные сертификаты и уже с их помощью подписывают конечные (end-entity) сертификаты для вашего домена.
Такая цепочка происходит очень часто и сертификат для нашего Патрика может быть подписан Губкой Бобом, а сертификат Губки Боба – Мистером Крабсом (а Губка Боб работает на мистера Крабса, как мы знаем из мультика). То есть это, в принципе, может быть вообще одна организация.
Для того, чтобы всё это провалидировать, клиенту нужно всю эту цепочку составить и проверить: взять ключик рутового сертификата, проверить, что промежуточный правильный и подпись валидна, потом взять ключик промежуточного сертификата и проверить конечный сертификат.
Цепочка должна присутствовать в списке сертификатов, которые сервер отсылает. По факту бывает всякое. Бывает, что не посылаются. Symantec, например, очень любил в бесплатные сертификаты не вставлять промежуточный. И поэтому в браузерах ничего не работало.
На самом деле, для большей производительности, поиск производится не по ишьюеру и субъекту, а с помощью расширений (extensions) – там вся эта информация лежит в виде хэшей, поэтому всё происходит быстрее. Но иногда хэшей нет, тогда происходит поиск по текстовым полям субъекта и ишьюера, и вот здесь могут случаться атаки.
Поэтому если вы видите сертификат, в котором этих полей нет, можно возбудиться и что-нибудь покопать – возможно, там всё не очень хорошо.
Ocsp stapling
Это примерно в ту же степь, но тут мы уже начинаем уходить от структуры Certificate Authority. То есть возлагаем эту проверку на сервер. Как это работает: сервер периодически ходит по этому OSCP URL-у и говорит: «OCSP-resolver, посмотри-ка, вот этот мой сертификат – в отозванных или нет?».
OCSP-resolver отвечает подписанным ответом, наш сервер его запоминает, и когда клиент приходит, ему отдаётся этот ответ. То есть у клиента есть подписанный ответ и в нём написано, что всё хорошо. Из-за того, что серверов меньше, чем клиентов и сервер может на какое-то время это кэшировать, нагрузка на всех становится меньше.
Тут есть тоже некоторые проблемы: текущий механизм — это только на один сертификат, а у нас цепочка (и это важно). А еще, из-за того, что мой сервер отвечать на это не обязан, клиент не может рассчитывать на то, что OSCP-stapling точно будет. И поэтому отказаться ни от CRL, ни от OCSP не получается – просто потому, что серверы не обязаны опрашивать и отвечать. Ну и еще один аспект: если в вебе с этим нормально, то в почте всё плохо, а в FTP даже слов таких не знают.
Проблема того, что сервер не обязан отвечать, может решиться добавлением в сертификат еще одного поля – так как оно еще не утряслось, у него просто номер, — в котором мы говорим клиенту, что сервер таки обязан ответить, и если он не ответил, считать этот сертификат невалидным.
Вот это всё вместе теоретически может начать работать, но пока еще мало распространено.
Google и Mozilla, так как им всё это не нравится, сделали свой CRL. И зашили его в браузер. Огонь вообще! Это работает быстро, ну и на этом все плюсы заканчиваются. Для того чтобы не помереть, они не запихивают в него DV-сертификаты. То есть если DV-сертификат отозван, они считают – ну и ладно.
Так что если DV-сертификат отозван и при этом нет OSCP-степлинга, Chrome об этом не скажет – он посчитает этот сертификат нормальным. На самом деле, правильно пользоваться OSCP-степлингом иOCSP Must Staple флагом в сертификате.
Инфраструктура открытых ключей. цепочка корневых сертификатов x509 v.3

Однако потом что-то пошло не так, кто-то оказался не готов, и использование ГОСТ Р 34.10-2001 продлили на 2021 год. Но вдруг все бросились переводить УЦ на ГОСТ Р 34.10-2021, а простых граждан переводить на новые сертификаты. У людей на руках стало по нескольку сертификатов. При проверки сертификатов или электронной подписи стали возникать вопросы, а где взять корневые сертификаты, чтобы установить в хранилища доверенных корневых сертификатов.
Это касается как хранилища сертификатов в Windows, так и хранилища сертификатов в браузерах Firefox и Google Chrome, GnuPG, LibreOffice, почтовых клиентах и даже OpenSSL. Конечно, надо было озаботиться этим при получении сертификата в УЦ и записать цепочку сертификатов на флешку. А с другой стороны у нас же цифровое общество и в любой момент должна быть возможность получить эту цепочку из сети. Как это сделать на страницах Хабра показалsimpleadmin. Однако для рядового гражданина это все же сложновато (особенно, если иметь ввиду, что абсолютное их большинство сидит на Windows): нужно иметь «какой-то» openssl, утилиту fetch, которой и у меня не оказалось на компьютере, и далеко не каждый знает, что вместо нее можно использовать wget. А сколько действий нужно выполнить. Выход конечно есть, написать скрипт, но не просто скрипт поверх openssl и иже с ним, а упакованный в самодостаточный выполняемый модуль для различных платформ.
На чем писать никаких сомнений не было – на Tcl и Python. И начинаем с Tcl и вот почему:
* охренительной вики, где есть даже игрушки (там можно подсмотреть интересное 🙂
* шпаргалки
* нормальные сборки tclkit (1.5 — 2 Мб как плата за реальную кросс-платформенность)
* и моя любимая сборка eTcl от Evolane (бережно сохранённая с умершего сайта 🙁
сохраняют высокий рейтинг Tcl/Tk в моём личном списке инструментария
и, да, wiki.tcl.tk/16867 (мелкий web-сервер с cgi на Tcl, периодически используется с завидным постоянством под tclkit)
а ещё — это просто красиво и красиво 🙂
К этому бы я добавил наличии утилиты
freewrap
, которая нам и поможет собрать автономные (standalone) утилиты для Linux и MS Windows. В результате мы будем иметь утилиту chainfromcert:
bash-4.3$ ./chainfromcert_linux64
Copyright(C)2021
Usage:
chainfromcert <file with certificate> <directory for chain certificate>
Bad usage!
bash-4.3$
В качестве параметров утилите задаются файл с пользовательским сертификатом (как в формате PEM, так и формате DER) и каталог, в котором будут сохранены сертификаты УЦ, входящие в цепочку:
bash-4.3$ ./chainfromcert_linux64 ./cert_test.der /tmp
Loading file: cert_test.der
Directory for chain: .
cert 1 from http://ca.ekey.ru/cdp/ekeyUC2021.cer
cert 2 from http://reestr-pki.ru/cdp/guc_gost12.crt
Goodby!
Length chain=2
Copyright(C) 2021
bash-4.3$
А теперь рассмотрим как работает утилита.
Информация о центре сертификации, выдавшем сертификат пользователю, хранится в расширении с oid-ом 1.3.6.1.5.5.7.1.1. В этом расширении может хранится как о местонахождении сертификата УЦ (oid 1.3.6.1.5.5.7.48.2), так и информация о службе OCSP УЦ (oid 1.3.6.1.5.5.7.48.1):
А информация, например, о периоде использования ключа электронной подписи хранится в расширении с oid-ом 2.5.29.16.
Для разбора сертификата и доступа к расширениям сертификата воспользуемся пакетом pki:
#!/usr/bin/tclsh -f
package require pki
Нам также потребуются пакет base64:
package require base64
Пакет pki, а также подгружаемые им пакет asn, и пакет base64 и помогут нам преобразовывать сертификаты из PEM-кодировки в DER-кодировку, разобрать ASN-структуры и собственно получить доступ к информации о местонахождении сертификатов УЦ.
Работа утилиты начинается с проверки параметров и загрузки файла с сертификатом:
proc usage {use } {
puts "Copyright(C) 2021-2021"
if {$use == 1} {
puts "Usage:nchainfromcert <file with certificate> <directory for chain certificate>n"
}
}
if {[llength $argv] != 2 } {
usage 1
puts "Bad usage!"
exit
}
set file [lindex $argv 0]
if {![file exists $file]} {
puts "File $file not exist"
usage 1
exit
}
puts "Loading file: $file"
set dir [lindex $argv 1]
if {![file exists $dir]} {
puts "Dir $dir not exist"
usage 1
exit
}
puts "Directory for chain: $dir"
set fd [open $file]
chan configure $fd -translation binary
set data [read $fd]
close $fd
if {$data == "" } {
puts "Bad file with certificate=$file"
usage 1
exit
}
Здесь все понятно и отметим только одно – файл с сертификатом рассматривается как бинарный файл:
chan configure $fd -translation binary
Это связано с тем, что сертификат может хранится как в формате DER (двоичный код), так и в формате PEM (base64 — кодировка).
После того как файл загружен вызывается процедура chainfromcert:
set depth [chainfromcert $data $dir]
которая собственно и загружает корневые сертификаты:
proc chainfromcert {cert dir} {
if {$cert == "" } {
exit
}
set asndata [cert_to_der $cert]
if {$asndata == "" } {
#Файл содержит все что угодно, только не сертификат
return -1
}
array set cert_parse [::pki::x509::parse_cert $asndata]
array set extcert $cert_parse(extensions)
if {![info exists extcert(1.3.6.1.5.5.7.1.1)]} {
#В сертификате нет расширений
return 0
}
set a [lindex $extcert(1.3.6.1.5.5.7.1.1) 0]
# if {$a == "false"} {
# puts $a
# }
#Читаем ASN1-последовательность расширения в Hex-кодировке
set b [lindex $extcert(1.3.6.1.5.5.7.1.1) 1]
#Переводим в двоичную кодировку
set c [binary format H* $b]
#Sequence 1.3.6.1.5.5.7.1.1
::asn::asnGetSequence c c_par_first
#Цикл перебора значений в засширении 1.3.6.1.5.5.7.1.1
while {[string length $c_par_first] > 0 } {
#Выбираем очередную последовательность (sequence)
::asn::asnGetSequence c_par_first c_par
#Выбираем oid из последовательности
::asn::asnGetObjectIdentifier c_par c_type
set tas1 [::pki::_oid_number_to_name $c_type]
#Выбираем установленное значение
::asn::asnGetContext c_par c_par_two
#Ищем oid с адресом корневого сертификата
if {$tas1 == "1.3.6.1.5.5.7.48.2" } {
#Читаем очередной корневой сертификат
set certca [readca $c_par $dir]
if {$certca == ""} {
#Прочитать сертификат не удалось. Ищем следующую точку с сертификатом
continue
} else {
global count
#Сохраняем корневой сертификат в указанном каталоге
set f [file join $dir [file tail $c_par]]
set fd [open $f w]
chan configure $fd -translation binary
puts -nonewline $fd $certca
close $fd
incr count
puts "cert $count from $c_par"
#ПОДЫМАЕМСЯ по ЦЕПОЧКЕ СЕРТИФИКАТОВ ВВЕРХ
chainfromcert $certca $dir
continue
}
} elseif {$tas1 == "1.3.6.1.5.5.7.48.1" } {
# puts "OCSP server (oid=$tas1)=$c_par"
}
}
# Цепочка закончилась
return $count
}
К комментариям добавить нечего, но у нас осталась не рассмотренной процедура readca:
proc readca {url dir} {
set cer ""
#Читаем сертификат в бинарном виде
if {[catch {set token [http::geturl $url -binary 1]
#получаем статус выполнения функции
set ere [http::status $token]
if {$ere == "ok"} {
#Получаем код возврата с которым был прочитан сертификат
set code [http::ncode $token]
if {$code == 200} {
#Сертификат успешно прочитан и будет созвращен
set cer [http::data $token]
} elseif {$code == 301 || $code == 302} {
#Сертификат перемещен в другое место, получаем его
set newURL [dict get [http::meta $token] Location]
#Читаем сертификат с другого сервера
set cer [readca $newURL $dir]
} else {
#Сертификат не удалось прочитать
set cer ""
}
}
} error]} {
#Сертификат не удалось прочитать, нет узла в сети
set cer ""
}
return $cer
}
Это процедура построена на использовании пакета http:
package require http
Для чтения сертификата мы используем следующую функцию:
set token [http::geturl $url -binary 1]
Назначение остальных используемых функции понятно из комментариев. Дадим только расшифровку кодов возврата для функции http::ncodel:
200 Запрос успешно выполнен
206 Запрос успешно выполнен, но удалось скачать только часть файла
301 Файл перемещен в другое место
302 Файл временно перемещен в другое место
401 Требуется аутентификация на сервере
403 Доступ к этому ресурсу запрещен
404 Указанный ресурс не может быть найден
500 Внутренняя ошибка
Осталось не рассмотренной одна процедура, а именно cert_to_der:
proc cert_to_der {data} {
set lines [split $data n]
set hlines 0
set total 0
set first 0
#Ищем PEM-сертификат в файле
foreach line $lines {
incr total
if {[regexp {^-----BEGIN CERTIFICATE-----$} $line]} {
if {$first} {
incr total -1
break
} else {
set first 1
incr hlines
}
}
if {[regexp {^(.*):(.*)$} $line ]} {
incr hlines
}
}
if { $first == 0 && [string range $data 0 0 ] == "0" } {
#Очень похоже на DER-кодировку "0" == 0x30
return $data
}
if {$first == 0} {return ""}
set block [join [lrange $lines $hlines [expr {$total-1}]]]
#from PEM to DER
set asnblock [base64::decode $block]
return $asnblock
}
Схема процедуры очень простая. Если это PEM-файл с сертификатом («—–BEGIN CERTIFICATE—– »), то выбирается тело этого файла и преобразуется в бинаоный код:
set asnblock [base64::decode $block]
Если это не PEM-файл, то проверяется это «похожесть» на asn-кодировку (нулевой бит должен быть равен 0x30).
Вот собственно и все, осталось добавить завершающие строки:
if {$depth == -1} {
puts "Bad file with certificate=$file"
usage 1
exit
}
puts "Goodby!nLength chain=$depth"
usage 0
exit
Теперь все собираем в один файл с именем
Проверить работу этого файла можно с помощью интерпретарора tclsh:
$ tclsh ./chainfromcert.tcl cert_orlov.der /tmp
Loading file: cert_test.der
Directory for chain: /tmp
cert 1 from http://ca.ekey.ru/cdp/ekeyUC2021.cer
cert 2 from http://reestr-pki.ru/cdp/guc_gost12.crt
Goodby!
Length chain=2
Copyright(C) 2021
$
В результате работы мы получили цепочку из двух сертификатов в каталоге /tmp.
Но мы хотели получить выполняемые модули для платформ Linux и Windowsи и чтобы пользователи не задумывались о каких-то интерпретаторах.
Для этой цели мы воспользуемся утилитой freewrapTCLSH. С помощью этой утилиты мы сделаем выполняемые модули нашей утилиты для платформ Linux и Windows как 32-х разрядных так и 64-х. Сборку утилит можно проводить для всех платформ на любой из платформ. Извините за тавтологию. Я буду собирать на linux_x86_64 (Mageia).
Для сборки потребуется:
1. Утилита freewrapTCLSH для платформы linux_x86_64;
2. Файл freewrapTCLSH с этой утилитой для каждой платформы:
— freewrapTCLSH_linux32
— freewrapTCLSH_linux64
— freewrapTCLSH_win32
— freewrapTCLSH_win64
3. Исходный файл нашей утилиты: chainfromcert.tcl
Итак, собираемый выполняемый файл chainfromcerty_linuxx86 для платформы Linux x86:
$freewrapTCLSH chainfromcert.tcl –w freewrapTCLSH_linux32 –o chainfromcerty_linuxx86
$
Сборка утилиты для платформы Windows 64-х битного выглядит так:
$freewrapTCLSH chainfromcert.tcl –w freewrapTCLSH_win64 –o chainfromcerty_win64.exe
$
И т.д. Утилиты готовы к использованию. Все необходимое для их работы они вобрали в себя.
Аналогичным образом пишется код и на Python-е.
В ближайшие дни я думаю дополнить пакет fsb795 (а он написан на Python-е) функцией получения цепочки корневых сертификатов.
Часть 1. самоподписанный сертификат
Для начала рассмотрим вариант самоподписанного сертификата корневого уровня.
Для упрощения задачи сгенерируем сертификат, который будет содержать только необходимые параметры:
Сделать это можно с помощью библиотеки Bouncy Castle, следующим образом:
private void button1_Click(object sender, EventArgs e)
{
var KeyGenerate = new RsaKeyPairGenerator();
KeyGenerate.Init(new KeyGenerationParameters(new SecureRandom(new CryptoApiRandomGenerator()), 1024));
AsymmetricCipherKeyPair kp = KeyGenerate.GenerateKeyPair();
var gen = new X509V3CertificateGenerator();
var certName = new X509Name("CN=CA");
var serialNo = new BigInteger("1",10);
gen.SetSerialNumber(serialNo);
gen.SetSubjectDN(certName);
gen.SetIssuerDN(certName);
gen.SetNotAfter(DateTime.Now.AddYears(100));
gen.SetNotBefore(DateTime.Now);
gen.SetSignatureAlgorithm("SHA1WITHRSA");
gen.SetPublicKey(kp.Public);
var myCert = gen.Generate(kp.Private);
byte[] result = DotNetUtilities.ToX509Certificate(myCert).Export(X509ContentType.Cert);
FileStream fs = new FileStream("D:\test1.crt", FileMode.CreateNew);
fs.Write(result, 0, result.Length);
fs.Flush();
fs.Close();
}
В результате выполнения данной процедуры будет создан стандартный x.509 сертификат, который, будучи открытым с помощью hex-редактора, выглядит вот таким чудесным образом:
30 82 01 8F 30 81 F9 A0 03 02 01 02 02 01 01 30
0D 06 09 2A 86 48 86 F7 0D 01 01 05 05 00 30 0D
31 0B 30 09 06 03 55 04 03 0C 02 43 41 30 20 17
0D 31 33 30 39 31 35 31 35 33 35 30 32 5A 18 0F
32 31 31 33 30 39 32 32 31 35 33 35 30 32 5A 30
0D 31 0B 30 09 06 03 55 04 03 0C 02 43 41 30 81
9F 30 0D 06 09 2A 86 48 86 F7 0D 01 01 01 05 00
03 81 8D 00 30 81 89 02 81 81 00 8D 80 B5 8E 80
8E 94 D1 04 03 6A 45 1A 54 5E 7E EE 6D 0C CB 0B
82 03 F1 7D C9 6F ED 52 02 B2 08 C3 48 D1 24 70
C3 50 C2 1C 40 BC B5 9D F8 E8 A8 41 16 7B 0B 34
1F 27 8D 32 2D 38 BA 18 A5 31 A9 E3 15 20 3D E4
0A DC D8 CD 42 B0 E3 66 53 85 21 7C 90 13 E9 F9
C9 26 5A F3 FF 8C A8 92 25 CD 23 08 69 F4 A2 F8
7B BF CD 45 E8 19 33 F1 AA E0 2B 92 31 22 34 60
27 2E D7 56 04 8B 1B 59 64 77 5F 02 03 01 00 01
30 0D 06 09 2A 86 48 86 F7 0D 01 01 05 05 00 03
81 81 00 0A 1C ED 77 F4 79 D5 EC 73 51 32 25 09
61 F7 00 C4 64 74 29 86 5B 67 F2 3D A9 39 34 6B
3C A9 92 B8 BF 07 13 0B A0 9B DF 41 E2 8A F6 D3
17 53 E1 BA 7F C0 D0 BC 10 B7 9B 63 4F 06 D0 7B
AC C6 FB CE 95 F7 8A 72 AA 10 EA B0 D1 6D 74 69
5E 20 68 5D 1A 66 28 C5 59 33 43 DB EE DA 00 80
99 5E DD 17 AC 43 36 1E D0 5B 06 0F 8C 6C 82 D3
BB 3E 2B A5 F1 94 FB 53 7B B0 54 22 6F F6 4C 18
1B 72 1C
Тот же самый сертификат, но уже открытый с помощью стандартных средств windows:
Имя сертификата CA
Издатель CA
Версия сертификата 3
Серийный номер 0x1
Недействителен до... 15.09.2021 15:35:00 GMT
Недействителен после... 22.09.2113 15:35:00 GMT
Цифровая подпись (SHA-1) F9 AD 58 B5 50 3D F6 36 5E B8 89 D4 DC C8 5F CC 25 4B 93 A2
Цифровая подпись (SHA-256) 42 02 24 20 4E 8F 3A 3E 31 38 88 E5 C5 E7 C3 03 14 3A A6 52 EA 78 B9 77 42 5B 99 EB 4B BA 23 82
Открытый ключ(1024 битный) Алгоритм открытого ключа rsaEncryption
Модуль
00: 8D 80 B5 8E 80 8E 94 D1 04 03 6A 45 1A 54 5E 7E
10: EE 6D 0C CB 0B 82 03 F1 7D C9 6F ED 52 02 B2 08
20: C3 48 D1 24 70 C3 50 C2 1C 40 BC B5 9D F8 E8 A8
30: 41 16 7B 0B 34 1F 27 8D 32 2D 38 BA 18 A5 31 A9
40: E3 15 20 3D E4 0A DC D8 CD 42 B0 E3 66 53 85 21
50: 7C 90 13 E9 F9 C9 26 5A F3 FF 8C A8 92 25 CD 23
60: 08 69 F4 A2 F8 7B BF CD 45 E8 19 33 F1 AA E0 2B
70: 92 31 22 34 60 27 2E D7 56 04 8B 1B 59 64 77 5F
Экспонента 01 00 01
Подпись Алгоритм подписи sha1WithRSAEncryption
Подпись
00: 0A 1C ED 77 F4 79 D5 EC 73 51 32 25 09 61 F7 00
10: C4 64 74 29 86 5B 67 F2 3D A9 39 34 6B 3C A9 92
20: B8 BF 07 13 0B A0 9B DF 41 E2 8A F6 D3 17 53 E1
30: BA 7F C0 D0 BC 10 B7 9B 63 4F 06 D0 7B AC C6 FB
40: CE 95 F7 8A 72 AA 10 EA B0 D1 6D 74 69 5E 20 68
50: 5D 1A 66 28 C5 59 33 43 DB EE DA 00 80 99 5E DD
60: 17 AC 43 36 1E D0 5B 06 0F 8C 6C 82 D3 BB 3E 2B
70: A5 F1 94 FB 53 7B B0 54 22 6F F6 4C 18 1B 72 1C
Имея два этих файла, один с двоичными данными, а другой с описанием сертификата, попробуем разобраться что здесь к чему.
Прежде всего, нужно отметить, что файл *.crt хранит информацию о сертификате в закодированном виде. Для кодирования применяется особый язык, называемый ASN.1.
ASN.1 — стандарт записи, описывающий структуры данных для представления, кодирования, передачи и декодирования данных. Wikipedia
С помощью языка ASN.1 можно описывать сложные структуры, состоящие из данных различных типов. Типичный пример ASN.1-файла выглядит как-то так:
Однако ASN.1 разрабатывался в те светлые времена, когда «640 КБ должно было хватать каждому» и тратить место на такую громоздкую запись не было никакой возможности. Поэтому, в целях экономии места, а также более удобной обработки хранимой в ASN.1-форме информации, был разработан специальный метод кодирования — DER.
DER-кодировка описывается следующим правилом. Первым записывается байт, характеризующий тип данных, затем последовательность байтов хранящих сведения о длине данных и затем уже записываются сами данные.
К примеру, для кодировки целого числа INTEGER 65537 используется следующая форма: 0203 01 00 01.Здесь первый байт 02, определяет тип INTEGER (полную таблицу типов вы можете найти например тут), второй байт 03 показывает длину блока. А следующие за этим байты 01 00 01, являются шестнадцатеричной записью нашего числа 65537.
В нашем случае, для описание простейшего самоподписаного сертификата, достаточно 9 типов данных. Приведем таблицу кодирования для этих типов:
Зная как кодируется каждый из этих типов, мы можем попытаться распарсить наш *.crt файл.
3082 01 8F3081 F9A0030201 02 0201 01 30
0D0609 2A 86 48 86 F7 0D 01 01 05 0500300D
310B30090603 55 04 03 0C02 43 41 302017
0D 31 33 30 39 31 35 31 35 33 35 30 32 5A 180F
32 31 31 33 30 39 32 32 31 35 33 35 30 32 5A 30
0D310B30090603 55 04 03 0C02 43 41 3081
9F 300D0609 2A 86 48 86 F7 0D 01 01 01 0500
0381 8D 00 3081 890281 81 00 8D 80 B5 8E 80
8E 94 D1 04 03 6A 45 1A 54 5E 7E EE 6D 0C CB 0B
82 03 F1 7D C9 6F ED 52 02 B2 08 C3 48 D1 24 70
C3 50 C2 1C 40 BC B5 9D F8 E8 A8 41 16 7B 0B 34
1F 27 8D 32 2D 38 BA 18 A5 31 A9 E3 15 20 3D E4
0A DC D8 CD 42 B0 E3 66 53 85 21 7C 90 13 E9 F9
C9 26 5A F3 FF 8C A8 92 25 CD 23 08 69 F4 A2 F8
7B BF CD 45 E8 19 33 F1 AA E0 2B 92 31 22 34 60
27 2E D7 56 04 8B 1B 59 64 77 5F 0203 01 00 01
300D0609 2A 86 48 86 F7 0D 01 01 05 050003
81 81 00 0A 1C ED 77 F4 79 D5 EC 73 51 32 25 09
61 F7 00 C4 64 74 29 86 5B 67 F2 3D A9 39 34 6B
3C A9 92 B8 BF 07 13 0B A0 9B DF 41 E2 8A F6 D3
17 53 E1 BA 7F C0 D0 BC 10 B7 9B 63 4F 06 D0 7B
AC C6 FB CE 95 F7 8A 72 AA 10 EA B0 D1 6D 74 69
5E 20 68 5D 1A 66 28 C5 59 33 43 DB EE DA 00 80
99 5E DD 17 AC 43 36 1E D0 5B 06 0F 8C 6C 82 D3
BB 3E 2B A5 F1 94 FB 53 7B B0 54 22 6F F6 4C 18
1B 72 1C
Преобразуя байты-идентификаторы типов и убирая байты описывающие длину блоков получим следующую структуру:
SEQUENCE(3 elem)
SEQUENCE(7 elem)
[0](1 elem)
INTEGER 2
INTEGER 1
SEQUENCE(2 elem)
OBJECT IDENTIFIER 1.2.840.113549.1.1.5
NULL
SEQUENCE(1 elem)
SET(1 elem)
SEQUENCE(2 elem)
OBJECT IDENTIFIER 2.5.4.3
UTF8String CA
SEQUENCE(2 elem)
UTCTime 13-09-15 15:35:02 UTC
GeneralizedTime 2113-09-22 15:35:02 UTC
SEQUENCE(1 elem)
SET(1 elem)
SEQUENCE(2 elem)
OBJECT IDENTIFIER 2.5.4.3
UTF8String CA
SEQUENCE(2 elem)
SEQUENCE(2 elem)
OBJECT IDENTIFIER 1.2.840.113549.1.1.1
NULL
BIT STRING(1 elem)
SEQUENCE(2 elem)
INTEGER 00: 8D 80 B5 8E 80 8E 94 D1 04 03 6A 45 1A 54 5E 7E
EE 6D 0C CB 0B 82 03 F1 7D C9 6F ED 52 02 B2 08
C3 48 D1 24 70 C3 50 C2 1C 40 BC B5 9D F8 E8 A8
41 16 7B 0B 34 1F 27 8D 32 2D 38 BA 18 A5 31 A9
E3 15 20 3D E4 0A DC D8 CD 42 B0 E3 66 53 85 21
7C 90 13 E9 F9 C9 26 5A F3 FF 8C A8 92 25 CD 23
08 69 F4 A2 F8 7B BF CD 45 E8 19 33 F1 AA E0 2B
92 31 22 34 60 27 2E D7 56 04 8B 1B 59 64 77 5F
INTEGER 65537
SEQUENCE(2 elem)
OBJECT IDENTIFIER 1.2.840.113549.1.1.5
NULL
BIT STRING 00: 0A 1C ED 77 F4 79 D5 EC 73 51 32 25 09 61 F7 00
C4 64 74 29 86 5B 67 F2 3D A9 39 34 6B 3C A9 92
B8 BF 07 13 0B A0 9B DF 41 E2 8A F6 D3 17 53 E1
BA 7F C0 D0 BC 10 B7 9B 63 4F 06 D0 7B AC C6 FB
CE 95 F7 8A 72 AA 10 EA B0 D1 6D 74 69 5E 20 68
5D 1A 66 28 C5 59 33 43 DB EE DA 00 80 99 5E DD
17 AC 43 36 1E D0 5B 06 0F 8C 6C 82 D3 BB 3E 2B
A5 F1 94 FB 53 7B B0 54 22 6F F6 4C 18 1B 72 1C
Это уже более похоже на то, что мы видим при открытии сертификатов в браузере или Windows. Пробежимся по каждому элементу:
Важным моментом, о котором стоит особенно упомянуть являются данные, для которых вычисляется подпись. Интуитивно может показаться, что подписываются все данные идущие до последнего поля BIT STRING, содержащего подпись. Но на самом деле это не так. В стандарте x.
SEQUENCE(7 elem)
[0](1 elem)
INTEGER 2
INTEGER 1
SEQUENCE(2 elem)
OBJECT IDENTIFIER 1.2.840.113549.1.1.5
NULL
SEQUENCE(1 elem)
SET(1 elem)
SEQUENCE(2 elem)
OBJECT IDENTIFIER 2.5.4.3
UTF8String CA
SEQUENCE(2 elem)
UTCTime 13-09-15 15:35:02 UTC
GeneralizedTime 2113-09-22 15:35:02 UTC
SEQUENCE(1 elem)
SET(1 elem)
SEQUENCE(2 elem)
OBJECT IDENTIFIER 2.5.4.3
UTF8String CA
SEQUENCE(2 elem)
SEQUENCE(2 elem)
OBJECT IDENTIFIER 1.2.840.113549.1.1.1
NULL
BIT STRING(1 elem)
SEQUENCE(2 elem)
INTEGER 00: 8D 80 B5 8E 80 8E 94 D1 04 03 6A 45 1A 54 5E 7E
EE 6D 0C CB 0B 82 03 F1 7D C9 6F ED 52 02 B2 08
C3 48 D1 24 70 C3 50 C2 1C 40 BC B5 9D F8 E8 A8
41 16 7B 0B 34 1F 27 8D 32 2D 38 BA 18 A5 31 A9
E3 15 20 3D E4 0A DC D8 CD 42 B0 E3 66 53 85 21
7C 90 13 E9 F9 C9 26 5A F3 FF 8C A8 92 25 CD 23
08 69 F4 A2 F8 7B BF CD 45 E8 19 33 F1 AA E0 2B
92 31 22 34 60 27 2E D7 56 04 8B 1B 59 64 77 5F
INTEGER 65537
Т.о. если перед вами будет стоять задача проверить ЭЦП x.509 сертификата, то для этого сперва необходимо извлечь TBS-сертификат.
Еще одно замечание относится к отпечатку сертификата. Как видите сам сертификат не содержит никаких сведений об отпечатке. Это объясняется тем, что отпечаток представляет собой обычное хеш-значение SHA-1 от всего файла сертификата, со всеми его полями, включая подпись издателя. Поэтому хранить отпечаток не обязательно, можно просто вычислять хеш при каждом просмотре сертификата.