Продолжение первой части, где мы развернули двухуровневую инфраструктуру: Root CA и три промежуточных центра — Person, Server и Code. В конце первой части я обещал, что мы научимся отзывать сертификаты и поднимем OCSP. Этим и займёмся.
Как и первая часть, статья задумана как подробное пособие: по каждой команде и расширению, которые мы используем, приведён расширенный справочник по используемым и смежным параметрам — с синтаксисом, допустимыми значениями, значениями по умолчанию и подводными камнями. Если какой-то ключ вам сейчас не нужен — пролистайте таблицу; она здесь, чтобы потом не лезть в man. Структура каждого раздела одинакова: сначала рабочие команды для типового случая, затем — справочник по всем параметрам.
Проверялось на версиях. Имена флагов, значения по умолчанию и синтаксис расширений сверены с официальной документацией OpenSSL master, а также nginx и Apache mod_ssl. OpenSSL живёт по веткам: то, что помечено как «OpenSSL 4.0 / master» (например, квалификатор
nonssуauthorityKeyIdentifier), в стабильных 3.x ещё недоступно. Если у вас OpenSSL 3.0–3.6 — сверяйте спорные опции командойopenssl <cmd> --helpили вmanсвоей версии, прежде чем копировать конфиг. Числовые коды ошибокopenssl verifyстарше 40 тоже менялись между ветками — проверяйте их по заголовку своей версии.
В этой части:
Чем отозванный сертификат отличается от просроченного и зачем тут два механизма — CRL и OCSP.
Добавляем в конфиги точки распространения (CDP) и AIA, чтобы выпускаемые сертификаты сами «говорили», где их проверять.
Отзываем сертификат и работаем с базой CA.
Генерируем CRL и разбираем его командой
openssl crl.Проверяем отзыв через
openssl verify.Поднимаем OCSP-responder: выпускаем для него сертификат, запускаем демон, проверяем статус.
Публикуем CRL и OCSP по HTTP (nginx), настраиваем OCSP stapling и проверку отзыва на стороне веб-сервера.
Все пути, имена файлов и секции конфигов — те же, что в Части 1. Где вы называли что-то иначе — подставьте свои значения.
Немного теории: CRL vs OCSP
Срок действия (notAfter) и отзыв — разные вещи. Сертификат может быть действующим по датам, но при этом скомпрометированным: уволился сотрудник, утёк приватный ключ, сменилась организация. Дата тут не поможет — нужен механизм, которым CA говорит всему миру «вот этому сертификату больше не верьте, хотя срок ещё не вышел».
Таких механизма два:
CRL (Certificate Revocation List) — подписанный CA список серийных номеров отозванных сертификатов. Клиент скачивает файл целиком и проверяет, нет ли в нём нужного серийника. Просто, работает офлайн (можно закэшировать), но список растёт и обновляется не мгновенно — у CRL есть
nextUpdate, до которого клиент имеет право пользоваться старой копией.OCSP (Online Certificate Status Protocol) — клиент спрашивает у responder’а статус одного конкретного сертификата по его серийному номеру и получает подписанный ответ
good/revoked/unknown. Свежее и легче для клиента, но требует доступного онлайн-сервиса.
На практике выпускают оба: в сам сертификат прописывают и адрес CRL (расширение crlDistributionPoints, CDP), и адрес OCSP (расширение authorityInfoAccess, AIA). Клиент сам решает, чем воспользоваться.
Важный момент, который сэкономит вам полчаса недоумения: расширения CDP и AIA попадают только в те сертификаты, что выпущены после их добавления в конфиг. Ранее выданные User1, certservice.info и т.д. этих ссылок внутри не несут — их придётся перевыпустить, если хотите, чтобы клиент находил точки проверки автоматически. Поэтому начнём именно с правки конфигов.
Шаг 1. Добавляем CDP и AIA в конфиги промежуточных CA
В Части 1 в конфигах уже были заготовлены секции [ crl_ext ] (для самого CRL) и [ ocsp ] (для будущего сертификата responder’а), но в расширениях конечных сертификатов ссылок на проверку не было. Добавим их.
Откройте /root/ca/config/PersonIntermediateCA.cnf и в секции, которой вы подписываете конечные сертификаты (в Части 1 это user_cert для пользователей; для серверов и кода — server_cert и code_cert), допишите две строки:
[ user_cert ] # ... то, что уже было из Части 1 ... basicConstraints = CA:FALSE keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment extendedKeyUsage = clientAuth, emailProtection # --- новое в Части 2: куда клиенту идти за статусом отзыва --- crlDistributionPoints = URI:http://pki.certservice.info/crl/PersonIntermediateCA.crl authorityInfoAccess = OCSP;URI:http://ocsp.certservice.info/
Несколько тонкостей синтаксиса, которые видно в справочнике ниже:
В
authorityInfoAccessразделитель между методом и адресом — точка с запятой (OCSP;URI:...), а между записями — запятая. Частая ошибка — поставить запятую вместо;.У
crlDistributionPointsдве формы. Короткая — один или несколько URI прямо в значении через запятую:
crlDistributionPoints = URI:http://pki.certservice.info/crl/PersonIntermediateCA.crl, URI:http://pki2.certservice.info/crl/PersonIntermediateCA.crl
Развёрнутая — когда нужны reasons или CRLIssuer: ссылка на одну секцию по имени, причём без префикса @ (это особенность именно CDP; у subjectAltName/issuingDistributionPoint, наоборот, пишут @секция):
crlDistributionPoints = cdp_person [ cdp_person ] fullname = URI:http://pki.certservice.info/crl/PersonIntermediateCA.crl reasons = keyCompromise, CACompromise
⚠️ Форма
crlDistributionPoints = @cdp1, @cdp2(список@-секций) для CDP не работает как ожидается. Если действительно нужно несколько reason-scoped DistributionPoints, проверьте синтаксис на своей версии OpenSSL (openssl req -textпосле генерации) или формируйте расширение через raw/DER-encoding.
AIA с несколькими записями (плюс адрес для скачивания самого сертификата издателя,
caIssuers) удобно расписать через секцию с индексами:
authorityInfoAccess = @aia [ aia ] OCSP;URI.0 = http://ocsp.certservice.info/ caIssuers;URI.0 = http://pki.certservice.info/certs/PersonIntermediateCA.cert.pem
Не помечайте CDP и AIA как
critical. По RFC 5280 эти расширения должны быть некритическими — иначе старые клиенты, не умеющие их обрабатывать, отвергнут весь сертификат.criticalуместен уissuingDistributionPoint(но он живёт в CRL, а не в сертификате).
То же самое — в [ server_cert ] и [ code_cert ] (и в ServerIntermediateCA.cnf / CodeIntermediateCA.cnf, поменяв имя CRL-файла на ServerIntermediateCA.crl и CodeIntermediateCA.crl).
После правки конфигов перевыпустите конечные сертификаты, которым нужны эти ссылки (процедура из Части 1). Проверить, что расширения легли в сертификат:
openssl x509 -in /root/ca/PersonIntermediateCA/certs/User1.cert.pem \ -noout -text | grep -A2 -E "CRL Distribution|Authority Information"
Ожидаем X509v3 CRL Distribution Points с нашим URI и Authority Information Access: OCSP - URI:....
Расширенный справочник: расширения X.509v3 для отзыва (CDP, AIA, IDP, Key Identifiers)
Все расширения и поля, которыми задаются точки распространения CRL и доступ к OCSP. Раздел документации — man5/x509v3_config.
Показать таблицу — 34 параметра (расширения X.509v3)
Параметр | Синтаксис / значения | По умолч. | Описание и нюансы |
|---|---|---|---|
| crlDistributionPoints = URI:http://example.com/myca.crl | crlDistributionPoints = cdp_section (имя секции без @)знач.: короткая форма: GeneralName(ы) через запятую (напр. URI:…); либо имя секции (без @) с полями fullname/relativename/CRLIssuer/reasons | — | Расширение CRL Distribution Points: указывает, откуда загружать список отозванных сертификатов (CRL). Поддерживает короткую форму (один или несколько GeneralName через запятую) и развёрнутую форму через секцию с полями fullname, relativename, CRLIssuer, reasons.⚠️ В короткой форме (name-value) формируется один DistributionPoint, где значение становится полем fullName, а поля reasons и CRLIssuer опускаются. Для нескольких DistributionPoint с reasons/CRLIssuer нужна развёрнутая форма через секцию(и). Типичная ошибка - забыть схему (URI:), указав голый URL. |
| fullname = URI:http://example.com/myca.crlзнач.: GeneralName в формате subject alternative name (URI:, dirName: и т.п.) | — | Поле секции DistributionPoint (для crlDistributionPoints и issuingDistributionPoint): полное имя точки распространения CRL. Имеет тот же формат, что и subject alternative name.⚠️ В man-странице пишется строчными буквами: fullname. Взаимоисключаемо с relativename внутри одного DistributionPoint - указывают либо fullname, либо relativename, но не оба. |
| relativename = section (значение - фрагмент DN, задаётся как DN-секция)знач.: фрагмент distinguished name (RDN), задаётся через секцию в формате DN | — | Поле секции DistributionPoint: имя точки распространения, заданное как фрагмент distinguished name; устанавливается как значение поля nameRelativeToCRLIssuer.⚠️ Значение берётся как фрагмент DN (формат как у distinguished name). Взаимоисключаемо с fullname. |
| CRLIssuer = dirName:issuer_sectзнач.: GeneralName (обычно dirName:секция) в формате subject alternative name | — | Поле секции DistributionPoint: имя издателя CRL, если CRL подписывается не самим издателем сертификата (indirect CRL). Значение в том же формате, что и subject alternative name.⚠️ В разных версиях документации OpenSSL встречается разный регистр имени поля (CRLIssuer и CRLissuer); после выпуска проверяйте |
| reasons = keyCompromise, CACompromiseзнач.: keyCompromise, CACompromise, affiliationChanged, superseded, cessationOfOperation, certificateHold, privilegeWithdrawn, AACompromise | — | Поле секции DistributionPoint: многозначное поле, содержащее причины отзыва (reason flags), которые покрывает данная точка распространения CRL. Перечисляются через запятую.⚠️ Точный перечень распознаваемых значений из man-страницы: keyCompromise, CACompromise, affiliationChanged, superseded, cessationOfOperation, certificateHold, privilegeWithdrawn, AACompromise. Регистр критичен: keyCompromise и CACompromise пишутся именно так. removeFromCRL в этот список не входит. |
| reasons = keyCompromise | — | Значение поля reasons / onlysomereasons: компрометация закрытого ключа субъекта.⚠️ Одно из распознаваемых значений reason flags. Регистр важен. |
| reasons = CACompromise | — | Значение поля reasons / onlysomereasons: компрометация ключа удостоверяющего центра (CA).⚠️ Пишется с заглавными CA: CACompromise. |
| reasons = affiliationChanged | — | Значение поля reasons / onlysomereasons: изменение принадлежности субъекта (например, смена организации).⚠️ Одно из распознаваемых значений reason flags. |
| reasons = superseded | — | Значение поля reasons / onlysomereasons: сертификат заменён новым.⚠️ Одно из распознаваемых значений reason flags. |
| reasons = cessationOfOperation | — | Значение поля reasons / onlysomereasons: прекращение деятельности/использования сертификата.⚠️ Одно из распознаваемых значений reason flags. |
| reasons = certificateHold | — | Значение поля reasons / onlysomereasons: временная приостановка действия сертификата (hold).⚠️ Одно из распознаваемых значений reason flags. |
| reasons = privilegeWithdrawn | — | Значение поля reasons / onlysomereasons: отзыв привилегий/полномочий субъекта.⚠️ Одно из распознаваемых значений reason flags. |
| reasons = AACompromise | — | Значение поля reasons / onlysomereasons: компрометация Attribute Authority (для атрибутных сертификатов).⚠️ Пишется AACompromise (две заглавные A). |
| authorityInfoAccess = OCSP;URI:http://ocsp.example.com/, caIssuers;URI:http://myca.example.com/ca.cer | authorityInfoAccess = @sectionзнач.: access_id;location (через запятую несколько записей); либо @секция | — | Расширение Authority Information Access (AIA): указывает, как получить сведения, связанные с сертификатом (статус и сертификат издателя). Синтаксис записи: access_id;location, где access_id - объектный идентификатор или известное имя, а location - GeneralName в формате subject alternative name.⚠️ location имеет тот же синтаксис, что и subject alternative name, за исключением того, что email:copy не поддерживается. Разделитель между access_id и location - точка с запятой ‘;’ (частая ошибка - поставить запятую). В @секции записи задаются с индексами (OCSP.0, OCSP.1, caIssuers.0) для нескольких значений одного типа. |
| OCSP;URI:http://ocsp.example.com/ | — | access_id в authorityInfoAccess: адрес OCSP-ответчика для проверки статуса сертификата в реальном времени.⚠️ Одно из известных имён метода доступа. После имени - ‘;’ и location (обычно URI:). |
| caIssuers;URI:http://myca.example.com/ca.cer | — | access_id в authorityInfoAccess: адрес, по которому можно загрузить сертификат(ы) издателя (CA) для достройки цепочки.⚠️ Часто указывает на DER-сертификат (.cer/.crt). Пишется caIssuers. |
| ad_timestamping;URI:… | — | access_id в authorityInfoAccess: метод доступа к службе меток времени (Time Stamping).⚠️ Одно из известных имён метода доступа, распознаваемых OpenSSL (подтверждено man-страницей). |
| AD_DVCS;URI:… | — | access_id в authorityInfoAccess: метод доступа Data Validation and Certification Server (DVCS).⚠️ Известное имя метода доступа (подтверждено man-страницей). |
| caRepository;URI:… | — | access_id (известное имя метода доступа, семантически относится к Subject Information Access): URI репозитория CA.⚠️ Распознаётся OpenSSL как известное имя метода доступа (подтверждено man-страницей). Семантически принадлежит subjectInfoAccess. |
| issuingDistributionPoint = critical, @idp_sectionзнач.: опционально critical; имя @секции с полями fullname/relativename/onlysomereasons/onlyuser/onlyCA/onlyAA/indirectCRL | — | Расширение Issuing Distribution Point (IDP): помещается только в CRL, описывает, к какой точке распространения относится данный CRL и какие типы сертификатов/причины он покрывает. Многозначное расширение, задаётся через секцию.⚠️ Это расширение должно появляться только в CRL (не в сертификате). По RFC 5280 IDP обычно помечается как critical. Синтаксис секции близок к DistributionPoint. |
| onlysomereasons = keyCompromise, CACompromiseзнач.: те же значения, что и reasons (keyCompromise, CACompromise, affiliationChanged, superseded, cessationOfOperation, certificateHold, privilegeWithdrawn, AACompromise) | — | Поле секции issuingDistributionPoint: ограничивает CRL только указанными причинами отзыва (onlySomeReasons).⚠️ Аналог поля reasons, но в контексте IDP. Перечисление через запятую. |
| onlyuser = TRUEзнач.: boolean (TRUE | FALSE) | — | Поле секции issuingDistributionPoint: CRL содержит только отзывы сертификатов конечных пользователей (onlyContainsUserCerts).⚠️ Согласно man-странице, значение каждого из onlyuser/onlyCA/onlyAA/indirectCRL - булево. |
| onlyCA = TRUEзнач.: boolean (TRUE | FALSE) | — | Поле секции issuingDistributionPoint: CRL содержит только отзывы сертификатов CA (onlyContainsCACerts).⚠️ Булево значение. |
| onlyAA = TRUEзнач.: boolean (TRUE | FALSE) | — | Поле секции issuingDistributionPoint: CRL содержит только отзывы атрибутных сертификатов (onlyContainsAttributeCerts).⚠️ Булево значение. |
| indirectCRL = TRUEзнач.: boolean (TRUE | FALSE) | — | Поле секции issuingDistributionPoint: указывает, что данный CRL является indirect CRL (издаётся не тем, кто выпустил сертификаты).⚠️ Булево значение. Связано с использованием CRLIssuer в DistributionPoint. |
| authorityKeyIdentifier = keyid[, issuer] | keyid:always, issuer:always | noneзнач.: none | keyid[:always][:nonss] | issuer[:always][:nonss] (через запятую) | — | Расширение Authority Key Identifier (AKID): идентифицирует ключ, которым подписан сертификат. Значение none означает, что AKID не включается. Иначе - список через запятую из ключевых слов keyid и/или issuer. keyid просит включить subject key identifier издателя; issuer просит включить серийный номер и distinguished name издателя.⚠️ Квалификатор always делает соответствующий элемент AKID обязательным - при невозможности включить элемент генерируется ошибка. Квалификатор nonss (OpenSSL 4.0/master) делает элемент условным: включается только если сертификат не самоподписанный. Конкретное значение default в man-странице не зафиксировано. |
| authorityKeyIdentifier = keyid | keyid:alwaysзнач.: keyid | keyid:always | keyid:nonss | — | Ключевое слово authorityKeyIdentifier: просит включить в AKID subject key identifier издателя. С :always включение обязательно (ошибка при невозможности); с :nonss - только если сертификат не самоподписанный.⚠️ Наиболее распространённый вариант: authorityKeyIdentifier = keyid:always. |
| authorityKeyIdentifier = issuer | issuer:alwaysзнач.: issuer | issuer:always | issuer:nonss | — | Ключевое слово authorityKeyIdentifier: просит включить в AKID серийный номер и distinguished name издателя (grandparent name сертификата субъекта).⚠️ Часто используют связку authorityKeyIdentifier = keyid:always,issuer:always. |
| keyid:always | issuer:always | — | Квалификатор для keyid/issuer в authorityKeyIdentifier: делает соответствующий элемент AKID обязательным.⚠️ При невозможности включить элемент генерация завершается ошибкой. |
| keyid:nonss | issuer:nonss | — | Квалификатор для keyid/issuer в authorityKeyIdentifier: делает соответствующий элемент AKID условным - он включается только если сертификат не самоподписанный (non self-signed).⚠️ Введён в OpenSSL 4.0 (ветка master); в стабильных 3.x недоступен — используйте always или опускайте квалификатор. |
| authorityKeyIdentifier = none | subjectKeyIdentifier = none | — | Значение для authorityKeyIdentifier и subjectKeyIdentifier: расширение не включается (AKID/SKID не добавляется).⚠️ Для AKID none означает, что AKID не включается; не комбинируется с keyid/issuer. Для SKID - расширение SKID не включается. |
| subjectKeyIdentifier = hash | none | знач.: none | hash | hex-строка (байты можно разделять ‘:’) | — | Расширение Subject Key Identifier (SKID): идентифицирует открытый ключ субъекта. Принимает три формы: none - расширение не включается; hash - keyIdentifier вычисляется как 160-битный SHA-1 хеш; шестнадцатеричная строка - используется указанное значение напрямую.⚠️ Hex-форма допускает разделители ‘:’ между байтами. Man-страница не объявляет hash значением по умолчанию - это три равноправные формы (default не документирован). |
| subjectKeyIdentifier = hash | — | Значение subjectKeyIdentifier: вычислить keyIdentifier как 160-битный SHA-1 хеш.⚠️ Одна из трёх форм SKID; man-страница не называет её значением по умолчанию. |
| extName = critical, | — | Префикс-флаг перед значением расширения: помечает расширение как критическое (critical). Используется, например, для issuingDistributionPoint.⚠️ Критичность задаётся явным словом critical в начале значения. По RFC 5280: AIA и crlDistributionPoints должны быть non-critical; issuingDistributionPoint обычно critical; SKID/AKID - non-critical. Ошибочная пометка AIA/CDP как critical ломает совместимость с клиентами. |
Версии и подводные камни:
freshestCRL (delta CRL distribution point, OID 2.5.29.46) в текущей man-странице x509v3_config отдельным ключевым словом не описан; при необходимости его задают числовым OID, синтаксис аналогичен crlDistributionPoints (по RFC 5280).
Квалификатор nonss (keyid:nonss, issuer:nonss) документирован в man-странице ветки master; согласно ей, он введён в OpenSSL 4.0, а в стабильных 3.x недоступен — на них используйте always или опускайте квалификатор.
Имена методов доступа OCSP, caIssuers, ad_timestamping, AD_DVCS, caRepository подтверждены man-страницей как известные access_id; при незнакомом методе используйте числовой OID в форме accessOID;location.
Регистр значений reasons критичен: keyCompromise, CACompromise, AACompromise пишутся именно так. Опечатка приводит к ошибке разбора конфига.
В authorityInfoAccess разделитель между access_id и location - точка с запятой ‘;’, между записями - запятая ‘,’. email:copy в location не поддерживается.
Man-страница не документирует значения по умолчанию для authorityKeyIdentifier и subjectKeyIdentifier, поэтому поля default оставлены пустыми.
Шаг 2. Отзываем сертификат и работаем с базой CA
Допустим, ключ пользователя User1 скомпрометирован. Отзываем (командой того CA, который этот сертификат выпускал, — Person Intermediate):
openssl ca -config /root/ca/config/PersonIntermediateCA.cnf \ -revoke /root/ca/PersonIntermediateCA/certs/User1.cert.pem \ -crl_reason keyCompromise
Флаг -crl_reason необязателен, но полезен — причина попадёт в CRL. Допустимые значения (дословно, регистр важен): unspecified, keyCompromise, CACompromise, affiliationChanged, superseded, cessationOfOperation, certificateHold, removeFromCRL. Для временной приостановки есть отдельный -crl_hold (с OID hold-инструкции), а для точного времени компрометации — -crl_compromise / -crl_CA_compromise.
После выполнения в index.txt запись поменяет статус с V (valid) на R (revoked), появятся дата отзыва и причина. Проверить можно прямо в базе, не открывая файл:
openssl ca -config /root/ca/config/PersonIntermediateCA.cnf -status 1002
(где 1002 — серийный номер сертификата в hex). А grep "^R" /root/ca/PersonIntermediateCA/index.txt покажет все отозванные.
Полезно понимать формат index.txt — это и есть «база отзыва», из которой строится CRL: шесть полей через табуляцию, первое — флаг статуса (V/R/E). Истёкшие сертификаты, к слову, не помечаются E автоматически — для этого есть openssl ca -updatedb.
На этом сертификат отозван в базе CA, но клиенты про это ещё не знают — отзыв «публикуется» только через CRL и OCSP. Это следующий шаг.
Расширенный справочник: openssl ca — отзыв, генерация CRL, конфиг и служебные файлы
Опции командной строки openssl ca, относящиеся к отзыву и CRL, плюс ключи секции [ CA_default ] и формат служебных файлов. Обратите внимание на написание: генерация CRL — это -gencrl (одно слово), секция расширений в командной строке — -crlexts, а одноимённый ключ конфига — crl_extensions.
Показать таблицу — 42 параметра (openssl ca: отзыв, CRL, конфиг, файлы)
Параметр | Синтаксис / значения | По умолч. | Описание и нюансы |
|---|---|---|---|
| -gencrl | — | Генерирует CRL на основе информации в файле базы (index.txt). Основная опция для выпуска списка отзыва. Требует хотя бы один из параметров срока (-crldays/-crlhours/-crlsec или default_crl_days/default_crl_hours в конфиге).⚠️ Критично: имя ровно -gencrl, частая ошибка — писать -gen_crl или -crlgen. Если crlnumber-файл присутствует, в CRL вставляется номер (расширение crlNumber). |
| -revoke filenameзнач.: filename — путь к файлу сертификата (PEM) | — | Отзывает сертификат из указанного файла: меняет его запись в index.txt со статуса V (valid) на R (revoked) и проставляет дату отзыва. Сопровождается -crl_reason / -crl_hold / -crl_compromise / -crl_CA_compromise.⚠️ Отзыв сам по себе не публикует CRL — после -revoke нужно отдельно выполнить -gencrl. Сертификат должен присутствовать в базе. |
| -valid filenameзнач.: filename — путь к файлу сертификата | — | Добавляет в базу запись о сертификате как о действительном (статус V) без его подписания. Для ручного приведения базы в соответствие.⚠️ Применять с осторожностью: запись вносится напрямую в index.txt без проверки политики. |
| -crl_reason reasonзнач.: unspecified, keyCompromise, CACompromise, affiliationChanged, superseded, cessationOfOperation, certificateHold, removeFromCRL | — | Указывает причину отзыва (CRL reason code). Сопоставление значений регистронезависимо. Установка любой причины делает CRL версии 2 (v2).⚠️ removeFromCRL и certificateHold имеют особую семантику. Имя ровно -crl_reason. |
| -crl_hold instructionзнач.: instruction — OID hold-инструкции (holdInstructionNone, holdInstructionCallIssuer, holdInstructionReject) | — | Устанавливает код причины certificateHold и hold instruction в указанный OID. Для временной приостановки (hold) сертификата.⚠️ Аргумент — именно OID. Делает CRL v2. |
| -crl_compromise timeзнач.: time в формате YYYYMMDDHHMMSSZ | — | Устанавливает причину keyCompromise и время компрометации в указанное значение.⚠️ Время в формате GeneralizedTime (4-значный год) с суффиксом Z (UTC). Делает CRL v2. |
| -crl_CA_compromise timeзнач.: time в формате YYYYMMDDHHMMSSZ | — | То же, что -crl_compromise, но причина устанавливается в CACompromise (компрометация ключа самого CA).⚠️ Обратите внимание на регистр в имени: -crl_CA_compromise (CA заглавными). |
| -crl_lastupdate timeзнач.: YYMMDDHHMMSSZ или YYYYMMDDHHMMSSZ | текущее время | Явно задаёт поле lastUpdate (thisUpdate) генерируемого CRL. Без опции — текущее время.⚠️ Принимает и UTCTime (2-значный год), и GeneralizedTime (4-значный год). |
| -crl_nextupdate timeзнач.: YYMMDDHHMMSSZ или YYYYMMDDHHMMSSZ | — | Явно задаёт поле nextUpdate генерируемого CRL. Если присутствует, значения -crldays/-crlhours/-crlsec игнорируются.⚠️ Взаимоисключает относительные сроки. |
| -crldays numзнач.: num — число дней | — | Число дней до следующего выпуска CRL — помещается в поле nextUpdate.⚠️ Конфиг-аналог: default_crl_days. Игнорируется при -crl_nextupdate. Складывается с -crlhours/-crlsec. |
| -crlhours numзнач.: num — число часов | — | Число часов до следующего выпуска CRL (добавляется к -crldays при вычислении nextUpdate).⚠️ Конфиг-аналог: default_crl_hours. Игнорируется при -crl_nextupdate. |
| -crlsec numзнач.: num — число секунд | — | Число секунд до следующего выпуска CRL (добавляется к -crldays/-crlhours).⚠️ Конфиг-аналога нет (только командная строка). Игнорируется при -crl_nextupdate. |
| -crlexts sectionзнач.: section — имя секции конфига | — | Секция конфига с расширениями CRL. Если секции нет — CRL v1; если есть (даже пустая) — CRL v2.⚠️ опция командной строки -crlexts (без подчёркивания после crl), а одноимённый ключ конфига — crl_extensions. Не путать. |
| -status serialзнач.: serial — серийный номер (hex) | — | Отображает статус отзыва сертификата с указанным серийным номером и завершает работу.⚠️ Удобно для проверки записи без открытия index.txt вручную. |
| -updatedb | — | Обновляет индекс: помечает истёкшие сертификаты, переводя статус с V на E (expired).⚠️ Без -updatedb истёкшие сертификаты остаются со статусом V (флаг E не проставляется автоматически). |
| -md algзнач.: alg — имя дайджеста (sha256, sha512, default, …) | — | Алгоритм хеширования для подписи. Применяется и к подписи CRL.⚠️ Конфиг-аналог: default_md. Для Ed25519/Ed448 дайджест не задаётся. Значение ‘default’ выбирает дайджест по типу ключа. |
| -rand_serial | — | Генерировать большой случайный серийный номер сертификата. Переопределяет использование файла serial.⚠️ На нумерацию CRL (crlnumber) не влияет. Для соответствия требованиям к энтропии серийников. |
| -notext | — | Не выводить текстовую форму сертификата в выходной файл — только PEM.⚠️ На содержимое CRL не влияет. |
| -config filenameзнач.: filename — путь к конфигу | — | Указывает используемый конфигурационный файл (openssl.cnf).⚠️ Без -config используется конфиг из окружения (OPENSSL_CONF / встроенный путь). |
| -name section (синоним -section section)знач.: section — имя секции конфига | — | Задаёт секцию конфига для использования (переопределяет default_ca в секции [ca]). -section — полный синоним.⚠️ Обычно секция называется CA_default и содержит все CRL/база-ключи (database, crlnumber, default_crl_days и т.д.). |
| -keyfile filename|uriзнач.: приватный ключ CA | — | Приватный ключ CA, которым подписываются запросы и CRL. Должен соответствовать -cert.⚠️ Конфиг-аналог: private_key. При -gencrl нужен для подписи списка отзыва. |
| -cert fileзнач.: сертификат CA | — | Сертификат CA (должен соответствовать ключу). Выступает издателем (issuer) для CRL.⚠️ Конфиг-аналог: certificate. |
| -outdir directoryзнач.: каталог вывода новых сертификатов | — | Каталог для новых сертификатов. Командный аналог конфиг-ключа new_certs_dir.⚠️ К CRL прямого отношения не имеет, но часть базовой инфраструктуры CA. |
| crlnumber = $dir/crlnumberзнач.: путь к файлу со следующим номером CRL (hex) | — | Файл со следующим номером CRL (hex). Номер вставляется в CRL (crlNumber) только если файл существует.⚠️ Опциональный ключ. Без файла CRL выпускается без crlNumber. Обычно инициализируется 1000 или 01. |
| crl = $dir/crl.pemзнач.: путь к файлу CRL | — | Исторический ключ, указывает расположение текущего CRL. Присутствует в типовых конфигах [CA_default].⚠️ В современных версиях менее значим, чем crlnumber/default_crl_days; результат обычно задаётся опцией вывода. |
| crl_extensions = crl_extзнач.: имя секции с расширениями CRL | — | Fallback-аналог опции -crlexts: секция с расширениями CRL. Есть секция (даже пустая) → CRL v2; нет → CRL v1.⚠️ ключ конфига crl_extensions (с подчёркиванием), опция командной строки -crlexts (без). Типовая секция содержит authorityKeyIdentifier. |
| default_crl_days = 30знач.: число дней | — | Конфиг-аналог -crldays. Используется, если в командной строке нет -crldays. Для генерации CRL нужен хотя бы один из default_crl_days/default_crl_hours.⚠️ Если ни дней, ни часов не задано — генерация CRL завершится ошибкой. |
| default_crl_hours = 24знач.: число часов | — | Конфиг-аналог -crlhours. Используется, если нет -crlhours. Хотя бы один из default_crl_days/default_crl_hours обязателен для генерации CRL.⚠️ Можно комбинировать с default_crl_days. |
| default_md = sha256знач.: имя дайджеста (sha256, sha512, default, …) | — | Конфиг-аналог -md. Алгоритм для подписи сертификатов и CRL. Обязательный (кроме Ed25519/Ed448).⚠️ Значение ‘default’ выбирает дайджест по типу ключа. md5/sha1 для подписи не рекомендуются. |
| database = $dir/index.txtзнач.: путь к текстовому файлу базы | — | Текстовый файл базы (index.txt). Обязательный. Может быть изначально пустым. Учёт выпущенных/отозванных, на его основе строится CRL.⚠️ Критичен: RESTRICTIONS предупреждают о трудности восстановления при повреждении. Рядом ведётся index.txt.old. |
| unique_subject = yesзнач.: yes | no | yes | yes — действительные записи (V) должны иметь уникальные subject; no — допускаются дубли subject. Для смены (roll-over) сертификатов CA рекомендуется no.⚠️ Фактическое значение фиксируется в файле index.txt.attr при добавлении первой записи. По умолчанию yes. |
| copy_extensions = noneзнач.: none | copy | copyall | none | Обработка расширений из CSR при выпуске: none — игнорировать; copy — копировать отсутствующие; copyall — копировать все.⚠️ Безопасность: copy/copyall позволяют запросу влиять на содержимое сертификата (SAN, basicConstraints) — осторожно. |
| serial = $dir/serialзнач.: путь к файлу серийного номера (hex) | — | Файл со следующим серийным номером сертификатов (hex). Обязательный. Не путать с crlnumber.⚠️ Переопределяется -rand_serial. Для CRL — отдельный файл crlnumber. |
| policy = policy_matchзнач.: имя секции политики | — | Конфиг-аналог -policy: секция политики CA (какие поля subject обязательны/совпадают). Обязательный ключ.⚠️ К CRL прямого отношения не имеет, но обязателен для работы CA-команды. |
| default_ca = CA_defaultзнач.: имя секции (обычно CA_default) | — | В секции [ca] указывает секцию по умолчанию с настройками CA. Переопределяется -name / -section.⚠️ Именно эта секция содержит все CRL/база-релевантные ключи. |
| \t\t\t\t\tзнач.: поля разделены табуляцией (TAB) | — | Шесть полей через TAB: 1 — флаг статуса (V/R/E/?); 2 — дата окончания [YY]YYMMDDHHMMSSZ; 3 — дата отзыва [YY]YYMMDDHHMMSSZ[,reason] (пусто, если не отозван); 4 — серийный номер (hex); 5 — имя файла или ‘unknown’; 6 — subject DN.⚠️ Формат не специфицирован в man (RESTRICTIONS лишь называет файл критическим); сверено по исходникам OpenSSL и PKI Tutorial. Разделитель — TAB. |
| Vзнач.: V | — | Статус Valid (действительный): сертификат выпущен и не отозван.⚠️ Истёкшие остаются V до |
| Rзнач.: R | — | Статус Revoked (отозванный). Поле 3 содержит дату отзыва (и опционально причину).⚠️ Устанавливается -revoke. В CRL попадают именно записи R. |
| Eзнач.: E | — | Статус Expired (истёкший): срок действия истёк.⚠️ Проставляется только после |
| ?знач.: ? | — | Служебный/недокументированный флаг неизвестного статуса. В нормальной эксплуатации не используется; обычно указывает на повреждённую запись.⚠️ В официальной документации как штатный статус не описан. При появлении ‘?’ проверьте целостность базы. |
| unique_subject = yes|noзнач.: файл из одной строки | unique_subject = yes | Файл атрибутов базы рядом с index.txt. Одна строка, отражающая unique_subject на момент добавления первой записи.⚠️ Значение фиксируется при первой записи и далее берётся из файла, а не из конфига. Для roll-over рекомендуется no. |
| знач.: одна строка — следующий номер CRL в hex (напр. 1000) | — | Файл, на который указывает конфиг-ключ crlnumber. При -gencrl его значение помещается в crlNumber, затем файл инкрементируется. Без файла — CRL без crlNumber.⚠️ Отдельный счётчик от serial. Обычно инициализируется 1000 или 01. |
Версии и подводные камни:
Man-страница openssl-ca (master) предупреждает: команда
caсоздавалась как ПРИМЕР и ‘не имеет качества продакшн-кода’; это однопользовательская команда без блокировки файлов — параллельный запуск над одной базой даёт непредсказуемый результат.Формат строки index.txt, флаги статуса и index.txt.attr не специфицированы в man (RESTRICTIONS лишь называет index.txt критическим). Семантика (6 полей, V/R/E, формат даты) сверена по исходникам OpenSSL и PKI Tutorial.
Флаг ‘?’ в index.txt в официальной документации как штатный статус не описан; относится к внутренней логике OpenSSL.
Для умолчаний ключей crlnumber, crl, default_crl_days/hours, default_md, database, serial поле default пустое, так как man не документирует встроенные значения (их задаёт пользователь). Документированные умолчания: unique_subject=yes, copy_extensions=none, -crl_lastupdate=текущее время.
Форматы дат различаются: -crl_compromise/-crl_CA_compromise требуют 4-значный год (YYYYMMDDHHMMSSZ), а -crl_lastupdate/-crl_nextupdate принимают и 2-значный, и 4-значный год.
Шаг 3. Генерируем и разбираем CRL
Теперь публикуем отзыв — генерируем CRL:
openssl ca -config /root/ca/config/PersonIntermediateCA.cnf \ -gencrl \ -out /root/ca/PersonIntermediateCA/crl/PersonIntermediateCA.crl.pem
Внимание к написанию: опция генерации CRL называется ровно
-gencrl(одно слово, без подчёркивания) — не-gen_crlи не-crlgen.
openssl ca берёт параметры CRL из секции [ CA_default ] (мы задали их ещё в Части 1):
crlnumber = $dir/crlnumber crl_extensions = crl_ext default_crl_days = 30 # срок жизни CRL: через 30 дней nextUpdate истечёт
При каждой генерации значение из файла crlnumber инкрементируется — это монотонный счётчик версий, по нему клиент отличает свежий CRL от старого. Срок до следующего обновления можно переопределить из командной строки (-crldays/-crlhours/-crlsec) или задать абсолютной датой (-crl_nextupdate).
Смотрим, что внутри, командой openssl crl:
openssl crl -in /root/ca/PersonIntermediateCA/crl/PersonIntermediateCA.crl.pem \ -noout -text
В выводе — Last Update / Next Update, наш X509v3 CRL Number, X509v3 Authority Key Identifier (его добавляет секция [ crl_ext ]) и список Revoked Certificates с серийником User1 и причиной Key Compromise. Той же командой CRL конвертируют в DER для публикации (-outform DER), вытаскивают отдельные поля (-lastupdate, -nextupdate, -crlnumber, -issuer) и проверяют подпись (-CAfile … -verify).
Расширенный справочник: openssl crl
Показать таблицу — 31 опция (openssl crl)
Параметр | Синтаксис / значения | По умолч. | Описание и нюансы |
|---|---|---|---|
| -help | — | Выводит краткую справку по использованию и список опций.⚠️ Печатает usage и завершает работу. |
| -inform DER|PEMзнач.: DER, PEM | — | Формат входного CRL. По документации формат по умолчанию не задан (unspecified).⚠️ Частая ошибка — читать DER без -inform DER. Лучше указывать явно. |
| -outform DER|PEMзнач.: DER, PEM | PEM | Формат выходного CRL. По умолчанию PEM.⚠️ Для публикации CRL в точках раздачи (CDP) обычно используют -outform DER. |
| -in filenameзнач.: filename | stdin | Входной файл. Без опции — стандартный ввод.⚠️ Совместно с -inform определяет интерпретацию входа. |
| -out filenameзнач.: filename | stdout | Выходной файл. По умолчанию стандартный вывод.⚠️ При информационных опциях без -noout вместе с ними печатается и закодированный CRL. |
| -dateopt rfc_822|iso_8601знач.: rfc_822, iso_8601 | rfc_822 | Формат вывода полей дат (lastUpdate/nextUpdate). По умолчанию rfc_822.⚠️ iso_8601 даёт машиночитаемый формат YYYY-MM-DD HH:MM:SSZ. |
| -key filenameзнач.: filename | — | Закрытый ключ для (пере)подписи CRL.⚠️ Формат ключа задаётся -keyform. |
| -keyform DER|PEM|P12знач.: DER, PEM, P12 | — | Формат файла закрытого ключа. По умолчанию не задан.⚠️ Применяется вместе с -key. |
| -text | — | Печатает CRL в текстовом (человекочитаемом) виде.⚠️ Показывает версию, издателя, даты, номер CRL, список отозванных с серийниками и расширениями. Без -noout печатается и закодированный CRL. |
| -hash | — | Выводит хеш имени издателя CRL — для поиска CRL в каталоге по издателю.⚠️ Печатает хеш имени издателя, не содержимого. Для каталога с именами .r0. |
| -hash_old | — | Хеш имени издателя по старому алгоритму (до OpenSSL 1.0.0).⚠️ Только для совместимости с каталогами под OpenSSL < 1.0.0. |
| -fingerprint | — | Выводит отпечаток (fingerprint) CRL.⚠️ Хеш самого CRL — для быстрой идентификации/сверки версии. |
| -crlnumber | — | Выводит номер CRL (поле crlNumber).⚠️ crlNumber монотонно растёт; полезен для контроля актуальности и дельта-CRL. |
| -issuer | — | Выводит имя издателя (issuer) CRL.⚠️ Формат отображения настраивается через -nameopt. |
| -lastupdate | — | Выводит поле lastUpdate (дата выпуска данного CRL).⚠️ Формат даты — через -dateopt. |
| -nextupdate | — | Выводит поле nextUpdate (дата ожидаемого следующего CRL).⚠️ По нему определяют, не устарел ли CRL. Формат — через -dateopt. |
| -noout | — | Не выводить закодированную версию CRL.⚠️ Используется с -text или отдельными инфо-опциями, чтобы получить только нужное без PEM/DER-дампа. |
| -nameopt optionзнач.: oneline, multiline, RFC2253, esc_ctrl, utf8 и др. (openssl-namedisplay-options) | — | Как отображаются имена субъекта/издателя.⚠️ Влияет на -issuer и текстовое представление издателя в -text. Значения через запятую. |
| -gendelta filenameзнач.: filename | — | Формирует дельту между основным CRL (из -in) и CRL из этого параметра.⚠️ Пропускается, если -verify не прошла проверку подписи. |
| -badsig | — | Портит подпись перед записью CRL; для тестирования.⚠️ Для негативного тестирования логики проверки подписи у потребителя. |
| -verify | — | Проверяет подпись CRL. При неудаче программа завершается (дальнейшая обработка, напр. -gendelta, пропускается). Неявно включается при -CApath/-CAfile/-CAstore.⚠️ Нужны сертификаты издателя CA через -CAfile/-CApath/-CAstore. |
| -CAfile fileзнач.: file | — | Файл с доверенными сертификатами CA (для проверки подписи CRL).⚠️ Указание неявно включает -verify. |
| -CApath dirзнач.: dir | — | Каталог с доверенными сертификатами CA (хешированный каталог).⚠️ Указание неявно включает -verify. Готовится через openssl rehash/c_rehash. |
| -CAstore uriзнач.: uri | — | URI хранилища доверенных сертификатов CA.⚠️ Указание неявно включает -verify. |
| -no-CAfile | — | Отключает встроенный по умолчанию файл доверенных CA.⚠️ Запрещает загрузку системного CAfile. |
| -no-CApath | — | Отключает встроенный по умолчанию каталог доверенных CA.⚠️ Запрещает загрузку системного CApath. |
| -no-CAstore | — | Отключает встроенное по умолчанию хранилище доверенных CA.⚠️ Запрещает загрузку системного CAstore. |
| -provider nameзнач.: name | — | Провайдер OpenSSL для загрузки.⚠️ Можно указывать несколько раз (default, legacy, fips). |
| -provider-path pathзнач.: path | — | Путь поиска провайдеров OpenSSL.⚠️ Может задаваться переменной OPENSSL_MODULES. |
| -propquery propqзнач.: propq | — | Строка запроса свойств для выбора реализаций алгоритмов у провайдеров.⚠️ Ограничивает выбор алгоритмов конкретным провайдером. |
| -provparam [name:]key=valueзнач.: [name:]key=value | — | Провайдер-специфичный параметр.⚠️ Для тонкой настройки конкретного провайдера. |
Версии и подводные камни:
Источник истины — man-страница master. Набор опций и формулировки могут отличаться в старых версиях (например, -CAstore/-no-CAstore, -dateopt и provider-опции отсутствуют до OpenSSL 3.0).
Опция -engine на странице openssl-crl(1) master не документирована; в сборках с engine может присутствовать как deprecated, но опираться не следует — используйте provider-опции.
Значения -keyform указаны как DER|PEM|P12; набор форматов может расширяться в зависимости от сборки.
Полные описания -CAfile/-CApath/-CAstore/-no-* и значений -nameopt даны ссылками на openssl-verification-options(1) и openssl-namedisplay-options(1).
Шаг 4. Проверяем отзыв через openssl verify
Прежде чем доверять отзыв веб-серверу, проверим его локально. Чище всего разделить роли: корень — единственный trust anchor (-CAfile), промежуточный — недоверенный для достройки цепочки (-untrusted), а CRL передать отдельно (-CRLfile):
openssl verify -crl_check \ -CAfile /root/ca/RootCA/certs/RootCA.cert.pem \ -untrusted /root/ca/PersonIntermediateCA/certs/PersonIntermediateCA.cert.pem \ -CRLfile /root/ca/PersonIntermediateCA/crl/PersonIntermediateCA.crl.pem \ /root/ca/PersonIntermediateCA/certs/User1.cert.pem
Так промежуточный CA не оказывается в trust store как доверенный корень — проверяется именно цепочка до Root CA. Если хочется быстро, без отдельных флагов, можно склеить всё в один bundle и подать через -CAfile, но привыкать к этому не стоит:
cat /root/ca/RootCA/certs/RootCA.cert.pem \ /root/ca/PersonIntermediateCA/certs/PersonIntermediateCA.cert.pem \ /root/ca/PersonIntermediateCA/crl/PersonIntermediateCA.crl.pem > /tmp/verify_bundle.pem openssl verify -crl_check -CAfile /tmp/verify_bundle.pem \ /root/ca/PersonIntermediateCA/certs/User1.cert.pem
Ключевой факт: по умолчанию openssl verify отзыв не проверяет вообще — нужен явный -crl_check (только лист) или -crl_check_all (вся цепочка). До отзыва команда печатает User1.cert.pem: OK. После отзыва и регенерации CRL — error 23 ... certificate revoked. Именно так увидит отозванный сертификат и веб-сервер.
Полезно знать коды ошибок, на которые вы будете натыкаться: 23 — сертификат отозван; 3 — не удалось получить CRL (забыли источник); 12 — CRL просрочен (nextUpdate в прошлом); 35 — у издателя CRL нет в keyUsage бита cRLSign. Полный список — в справочнике.
openssl verifyпроверяет отзыв только по CRL. Онлайн-OCSP в этой команде нет — для OCSP используется отдельная командаopenssl ocsp(Шаг 5) или OCSP stapling вopenssl s_client.
Расширенный справочник: опции отзыва openssl verify и коды ошибок
Показать таблицу — 45 строк (опции openssl verify + коды ошибок)
Параметр | Синтаксис / значения | По умолч. | Описание и нюансы |
|---|---|---|---|
| -crl_check | выключено (отзыв не проверяется) | Включает проверку отзыва только для конечного (листового) сертификата через поиск действующего CRL. Если действующий CRL не найден — ошибка.⚠️ Проверяется только лист. Источник CRL: -CRLfile, -crl_download (по CDP) или CRL в trust store. Без источника — ошибка 3 (unable to get certificate CRL). |
| -crl_check_all | выключено | Включает проверку отзыва для всех сертификатов цепочки через поиск действующих CRL для каждого.⚠️ Отличие от -crl_check: весь путь, включая промежуточные CA (для каждого нужен свой CRL). Для полноценной проверки PKI нужен именно -crl_check_all. |
| -crl_download | выключено | Пытается скачать CRL для сертификатов по их записям CDP (CRL Distribution Points).⚠️ Требует расширения CDP с доступным URL. Имеет смысл только вместе с -crl_check/-crl_check_all. Без CDP/доступа — ошибка 3. |
| -CRLfile filename|uriзнач.: файл/URI с одним или несколькими CRL в PEM или DER | — | Файл или URI с одним или несколькими CRL (PEM/DER) для проверки отзыва.⚠️ Можно указывать несколько раз. Сам по себе не включает проверку — нужен -crl_check/-crl_check_all. |
| -extended_crl | выключено | Включает расширенные возможности CRL: косвенные (indirect) CRL и альтернативные ключи подписи CRL.⚠️ Нужно, когда CRL подписан делегированным ключом или используется indirect CRL. Без неё — возможна ошибка 33. |
| -use_deltas | выключено | Включает поддержку дельта-CRL (delta CRLs).⚠️ Дельта-CRL содержит только изменения относительно базового. Без опции дельты игнорируются. |
| -ignore_critical | выключено (неизвестные критические расширения → отклонение) | Обычно неподдерживаемое критическое расширение приводит к отклонению (RFC 5280). С этой опцией такие расширения игнорируются.⚠️ Снижает строгость. Подавляет ошибки 34 и 36. |
| -x509_strict | выключено | Отключает обходные приёмы для некорректных сертификатов; генерирует ошибки на несоответствии RFC 5280.⚠️ Повышает строгость — может выявить нарушения в полях, относящихся к CRL и keyUsage. |
| -no_check_time | проверка времени включена | Подавляет проверку срока действия сертификатов И CRL относительно текущего времени. При заданном -attime подавление не применяется.⚠️ Подавляет ошибки 11 (CRL is not yet valid) и 12 (CRL has expired). |
| -attime timestampзнач.: timestamp — секунды с 1 января 1970 (Unix Epoch) | текущее системное время | Проверки достоверности на заданный момент времени, а не на текущее.⚠️ Влияет и на проверку актуальности CRL (lastUpdate/nextUpdate сверяются с этим временем). |
| -check_ss_sig | выключено | Проверяет подпись последнего сертификата цепочки, если он явно самоподписанный.⚠️ Относится к построению цепочки (корень), не напрямую к CRL. |
| -partial_chain | выключено | Разрешает успех при неполной (частичной) цепочке.⚠️ Позволяет доверять промежуточному из trust store без построения до корня. При частичной цепочке отзыв проверяется только до точки доверия. |
| -trusted_first | включено всегда (нельзя отключить) | Доверенные сертификаты используются раньше любых из -untrusted. Включена по умолчанию и не может быть отключена.⚠️ Сохранена для совместимости. Влияет на выбор издателя, а значит и на то, чей CRL ищется. |
| -no_alt_chains | — | Историческая опция поиска альтернативных цепочек. В современных версиях фактически без эффекта.⚠️ Присутствует в SYNOPSIS для совместимости. |
| -CAfile fileзнач.: файл с доверенными сертификатами (DER — один; PEM — несколько) | системный файл, если не -no-CAfile | Загружает файл с доверенным сертификатом (DER) или несколькими (PEM).⚠️ Формирует корень доверия, относительно которого строится цепочка и ищутся CRL издателей. |
| -CApath dirзнач.: каталог-хранилище (hashed dir) | системный каталог, если не -no-CApath | Каталог доверенных сертификатов как trust store.⚠️ Файлы индексируются по хэшу (openssl rehash). Там же могут лежать CRL. |
| -CAstore uriзнач.: URI хранилища доверенных сертификатов | системное хранилище, если не -no-CAstore | Использует uri как хранилище доверенных сертификатов.⚠️ Обобщённый механизм OSSL_STORE. |
| -no-CAfile | -no-CApath | -no-CAstore | — | Не загружать стандартный файл/каталог/хранилище доверенных сертификатов.⚠️ Полезно, чтобы изолировать проверку только заданными вручную корнями. |
| -untrusted filename|uriзнач.: файл/URI с сертификатами (PEM) | — | Недоверенные сертификаты для построения цепочки (например, промежуточные CA).⚠️ Можно несколько раз. Доверенными сами не становятся, но достраивают путь; при -crl_check_all для них тоже ищется CRL. |
| -trusted filename|uriзнач.: файл/URI с сертификатами (PEM) | — | (Более-менее) доверенные сертификаты; считается доверенным при подходящем положительном атрибуте доверия.⚠️ Можно несколько раз. Альтернатива -CAfile/-CApath для задания корней. |
| -show_chain | выключено | Показывает построенную цепочку (при успехе). Недоверенные помечаются.⚠️ Помогает понять, какой путь выбран и чьи CRL должны проверяться. |
| -verbose | выключено | Печатает дополнительную информацию о выполняемых операциях.⚠️ Полезно для диагностики проблем с поиском/загрузкой CRL. |
| -policy_check ; -policy OID ; -explicit_policy ; -inhibit_any ; -inhibit_map ; -policy_printзнач.: OID для -policy | — | Подсистема обработки политик сертификации (RFC 5280): включение, добавление OID в user-initial-policy-set и установка соответствующих переменных политики.⚠️ К отзыву прямого отношения не имеют, относятся к строгой валидации цепочки. Приведены для полноты. |
| -purpose purposeзнач.: sslclient, sslserver, nssslserver, smimesign, smimeencrypt, crlsign, ocsphelper, timestampsign, codesign, any | — | Высокоуровневое назначение целевого сертификата; проверяет пригодность для этой цели.⚠️ При несовпадении — ошибка 26 (unsuitable certificate purpose). Назначение crlsign относится к подписанию CRL. |
| -issuer_checks | — | Игнорируется (no-op).⚠️ Сохранена для совместимости со старыми скриптами. |
| -allow_proxy_certs | выключено | Разрешает верификацию прокси-сертификатов.⚠️ При запрете — ошибка 40 (proxy certificates not allowed). |
| -auth_level levelзнач.: level — целое (уровень безопасности) | — | Устанавливает уровень безопасности аутентификации цепочки.⚠️ Влияет на допустимые алгоритмы/длины ключей при валидации. |
| -verify_depth numзнач.: num — макс. число промежуточных CA | — | Ограничивает цепочку num промежуточными CA-сертификатами.⚠️ При превышении — ошибка 22 (certificate chain too long). |
| -vfyopt nm:vзнач.: пара имя:значение (алгоритм-специфично) | — | Передаёт опции алгоритму подписи при верификации.⚠️ Опция самой команды openssl verify. |
| verify error 0 (X509_V_OK)знач.: ok | — | Успешная проверка: ошибок нет, отзыв (если проверялся) пройден.⚠️ Текст: «ok». Код 0. |
| verify error 2знач.: unable to get issuer certificate | — | Не найден издатель — цепочку нельзя достроить до доверенного корня.⚠️ Часто отсутствует промежуточный в -untrusted или корень в -CAfile/-CApath. |
| verify error 3знач.: unable to get certificate CRL | — | Не удалось получить CRL, хотя проверка отзыва запрошена. CRL не найден среди источников и не скачан.⚠️ Самая частая ошибка при включении отзыва без корректного источника CRL: проверьте -CRLfile, -crl_download (и CDP), либо CRL в trust store. |
| verify error 5знач.: unable to decrypt CRL’s signature | — | Не удалось проверить подпись CRL ожидаемым открытым ключом.⚠️ Аналог кода 4, но для CRL. |
| verify error 8знач.: CRL signature failure | — | Подпись CRL некорректна (CRL подписан не тем ключом или повреждён).⚠️ Найденный CRL не подписан ожидаемым издателем/ключом. |
| verify error 11знач.: CRL is not yet valid | — | CRL ещё не вступил в силу: lastUpdate в будущем относительно проверочного времени.⚠️ Часто признак рассинхронизации часов. Подавляется -no_check_time. |
| verify error 12знач.: CRL has expired | — | Срок действия CRL истёк: nextUpdate в прошлом — CRL устарел.⚠️ Нужен свежий CRL. Подавляется -no_check_time (но это снижает безопасность). |
| verify error 15знач.: format error in CRL’s lastUpdate field | — | Ошибка формата поля lastUpdate CRL — дату не удалось разобрать.⚠️ Текст: «format error in CRL’s lastUpdate field». |
| verify error 16знач.: format error in CRL’s nextUpdate field | — | Ошибка формата поля nextUpdate CRL — дату не удалось разобрать.⚠️ Текст: «format error in CRL’s nextUpdate field». |
| verify error 23знач.: certificate revoked | — | ГЛАВНЫЙ код отзыва: сертификат отозван — присутствует в CRL.⚠️ Появляется только при включённой проверке (-crl_check/-crl_check_all) и доступном CRL с серийником сертификата. |
| verify error 33знач.: unable to get CRL issuer certificate | — | Не найден сертификат издателя CRL — нельзя сопоставить CRL с подписавшим CA.⚠️ Для indirect/делегированных CRL; может потребоваться -extended_crl и нужный сертификат издателя CRL. |
| verify error 34знач.: unhandled critical extension | — | В сертификате критическое расширение, не поддерживаемое OpenSSL; по RFC 5280 отклоняется.⚠️ Подавляется -ignore_critical (с потерей строгости). |
| verify error 35знач.: key usage does not include CRL signing | — | Сертификат, которым проверяют подпись CRL, не имеет в keyUsage бита cRLSign.⚠️ Прямо относится к отзыву: издатель CRL не уполномочен подписывать CRL. |
| verify error 36знач.: unhandled critical CRL extension | — | В самом CRL критическое расширение, не поддерживаемое OpenSSL; CRL отклоняется.⚠️ Подавляется -ignore_critical. Связан с -extended_crl при нестандартных расширениях CRL. |
| verify error 44знач.: different CRL scope | — | Область действия (scope) найденного CRL не соответствует проверяемому сертификату.⚠️ При partitioned/indirect CRL и несовпадении IDP. Может потребоваться -extended_crl. |
| verify error 54знач.: CRL path validation error | — | Ошибка валидации пути для CRL: не удалось построить/проверить отдельный путь к издателю CRL.⚠️ При indirect CRL / альтернативном пути подписи (см. -extended_crl). |
Версии и подводные камни:
Числовые коды сверены по include/openssl/x509_vfy.h.in, тексты — по crypto/x509/x509_txt.c (X509_verify_cert_error_string). Сами man-страницы не содержат полного списка кодов с номерами — отсылают к X509_STORE_CTX_get_error(3) и заголовку <openssl/x509_vfy.h>.
Нумерация старших кодов (40+) менялась между версиями (напр. X509_V_ERR_INVALID_CA в master = 79, исторически 24). Если статья ориентируется на конкретную версию (1.1.1, 3.0) — перепроверьте 44, 54, 79 по заголовку той версии. CRL-коды 3, 5, 8, 11, 12, 15, 16, 23, 33, 34, 35, 36 стабильны.
openssl verify проверяет отзыв только по CRL. Онлайн-OCSP в этой команде нет — для OCSP используется openssl ocsp (или OCSP stapling в openssl s_client).
Перечень -purpose и политики (-policy и т.д.) приведены для полноты построения/валидации цепочки; к отзыву относятся лишь косвенно.
Шаг 5. Поднимаем OCSP-responder
CRL — «скачай весь список». OCSP — «спроси про один сертификат». Чтобы ответы responder’а были доверенными, ему нужен собственный сертификат с расширением OCSPSigning. Шаблон расширений у нас готов — секция [ ocsp ] из Части 1.
5.1. Ключ и сертификат для responder’а
Responder работает демоном, поэтому ключ обычно делают без пароля — иначе сервис не стартует без ввода passphrase. Это компромисс: храните ключ с правами 400.
⚠️ В Части 1 секция
[ ocsp ]содержалаbasicConstraints,subjectKeyIdentifier,authorityKeyIdentifier,keyUsageиextendedKeyUsage, но неnoCheck. Если взять её как есть, сертификат responder’а выйдет без OCSP No Check. Перед выпуском приведите секцию к такому виду:
[ ocsp ] basicConstraints = CA:FALSE subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer keyUsage = critical, digitalSignature extendedKeyUsage = critical, OCSPSigning noCheck = ignored
OpenSSL документирует noCheck как строковое расширение: значение справа (ignored) парсится, но игнорируется — важно само присутствие ключа.
# Приватный ключ responder'а (без пароля — для автозапуска) openssl genrsa \ -out /root/ca/PersonIntermediateCA/private/ocsp.person.key.pem 2048 chmod 400 /root/ca/PersonIntermediateCA/private/ocsp.person.key.pem # Запрос на сертификат (CN — имя responder'а, напр. ocsp.certservice.info) openssl req -config /root/ca/config/PersonIntermediateCA.cnf \ -new -sha256 \ -key /root/ca/PersonIntermediateCA/private/ocsp.person.key.pem \ -out /root/ca/PersonIntermediateCA/csr/ocsp.person.csr.pem # Подписываем промежуточным CA, обязательно с -extensions ocsp openssl ca -config /root/ca/config/PersonIntermediateCA.cnf \ -extensions ocsp -days 365 -notext -md sha256 \ -in /root/ca/PersonIntermediateCA/csr/ocsp.person.csr.pem \ -out /root/ca/PersonIntermediateCA/certs/ocsp.person.cert.pem
Проверим, что EKU OCSP Signing на месте:
openssl x509 -in /root/ca/PersonIntermediateCA/certs/ocsp.person.cert.pem \ -noout -text | grep -A1 "Extended Key Usage"
Два расширения в этом сертификате — критичные для доверия:
extendedKeyUsage = critical, OCSPSigning— по RFC 6960 клиент признаёт сертификат делегированным подписантом OCSP только при наличииOCSPSigningи при том, что сертификат выпущен тем же CA, чьи сертификаты он подтверждает.noCheck(расширениеid-pkix-ocsp-nocheck, OID1.3.6.1.5.5.7.48.1.5) — говорит клиенту «статус самого сертификата responder’а проверять не нужно», разрывая рекурсию «кто проверит проверяющего». Раз статус не проверяется — такой сертификат делают короткоживущим.
Расширенный справочник: расширения сертификата OCSP-responder (секция [ ocsp ])
Показать таблицу — 18 параметров (сертификат OCSP-responder)
Параметр | Синтаксис / значения | По умолч. | Описание и нюансы |
|---|---|---|---|
| basicConstraints = [critical,] CA:(TRUE|FALSE) [, pathlen:nonnegative_integer]знач.: CA:TRUE | CA:FALSE; опционально pathlen:nonnegative_integer (только вместе с CA:TRUE) | — | Многозначное расширение, указывающее, является ли сертификат сертификатом CA. Дословно: ‘This is a multi-valued extension which indicates whether a certificate is a CA certificate.’ Для сертификата OCSP-responder это конечный (end-entity) сертификат, поэтому ставят CA:FALSE либо вовсе опускают расширение.⚠️ Критично по профилю. Документация: ‘An end-user certificate must either have CA:FALSE or omit the extension entirely.’ pathlen применим только с CA:TRUE и для responder неуместен. Типичная ошибка - выдать responder с CA:TRUE. |
| keyUsage = [critical,] name[, name…]знач.: digitalSignature, nonRepudiation, contentCommitment, keyEncipherment, dataEncipherment, keyAgreement, keyCertSign, cRLSign, encipherOnly, decipherOnly | — | Многозначное расширение, задающее криптографическое назначение ключа (список имён через запятую). Дословно: ‘Key usage is a multi-valued extension consisting of a list of names of the permitted key usages.’ Для OCSP-responder ключ используется для подписи OCSP-ответов, поэтому нужен digitalSignature. Рекомендуемая запись: keyUsage = critical, digitalSignature.⚠️ Критично: для responder указывают critical и digitalSignature. В документации nonRepudiation и contentCommitment перечислены как взаимозаменяемые имена для одного бита. Значения keyEncipherment/keyCertSign/cRLSign для responder не нужны и являются ошибкой профиля. |
| keyUsage = critical, digitalSignatureзнач.: critical | — | Префиксное слово в начале значения расширения, помечающее расширение как критическое. Документация: ‘If critical is present then the extension will be marked as critical.’ Для keyUsage и extendedKeyUsage сертификата responder его указывают явно.⚠️ Документация не задаёт критичность по умолчанию для отдельных расширений - она определяется только наличием слова critical. Для keyUsage/extendedKeyUsage у responder практически всегда ставят critical; SKID/AKID обычно оставляют некритичными. |
| extendedKeyUsage = [critical,] value[, value…]знач.: serverAuth, clientAuth, codeSigning, emailProtection, timeStamping, OCSPSigning, ipsecIKE, msCodeInd, msCodeCom, msCTLSign, msEFS, а также OID в точечной нотации | — | Указывает один или несколько целевых сценариев применения ключа. Дословно: ‘This extension consists of a list of values indicating purposes for which the certificate public key can be used.’ Для делегированного OCSP-responder обязательно присутствие OCSPSigning: по RFC 6960 клиент признаёт сертификат делегированным подписантом OCSP только при наличии id-kp-OCSPSigning. Рекомендуемая запись: extendedKeyUsage = critical, OCSPSigning.⚠️ Ключевое расширение делегирования. Список имён (serverAuth, clientAuth, codeSigning, emailProtection, timeStamping, OCSPSigning, ipsecIKE, msCodeInd, msCodeCom, msCTLSign, msEFS) и допустимость произвольных OID подтверждены документацией master. Обычно в сертификате responder указывают только OCSPSigning. |
| extendedKeyUsage = critical, OCSPSigningзнач.: OCSPSigning | — | Значение EKU ‘OCSP Signing’ (id-kp-OCSPSigning, OID 1.3.6.1.5.5.7.3.9). Добавленное в сертификат самим CA, оно авторизует responder подписывать OCSP-ответы от имени этого CA.⚠️ Имя OCSPSigning документировано в x509v3_config. OID 1.3.6.1.5.5.7.3.9 происходит из RFC 6960/5280, а не из man-страницы x509v3_config. Сертификат должен быть выдан тем же CA, чьи сертификаты он подтверждает. |
| subjectKeyIdentifier = hashзнач.: hash | none | <шестнадцатеричная строка> | — | Идентификатор открытого ключа субъекта. При значении hash вычисляется 160-битный SHA-1 хэш. Документация: ‘The keyIdentifier is composed of the 160-bit SHA-1 hash of the value of the BIT STRING subjectPublicKey (excluding the tag, length, and number of unused bits).’ none - расширение не включается; шестнадцатеричная строка используется напрямую (настоятельно не рекомендуется).⚠️ Практически всегда используют значение hash. Прямое указание hex-строки документация называет ‘strongly discouraged’. Обычно расширение некритично. |
| authorityKeyIdentifier = keyid[:always|:nonss][, issuer[:always|:nonss]]знач.: keyid, issuer (каждое с опциональным суффиксом :always или :nonss); none - не включать расширение | — | Идентификатор ключа CA, выпустившего сертификат responder. Рекомендуемая запись: authorityKeyIdentifier = keyid, issuer. Документация: keyword keyid ‘asks that the AKID include the issuer’s subject key identifier’; keyword issuer ‘asks that the AKID include the serial number and issuer distinguished name … of the issuer certificate’.⚠️ Документация: суффикс always делает элемент обязательным (‘an error is raised if that element cannot be included’); суффикс nonss делает элемент условным при условии, что сертификат не самоподписан (‘further conditional on the certificate not being self-signed’). Значение none отключает расширение. Обычно некритично. |
| authorityKeyIdentifier = keyid[:always|:nonss]знач.: keyid, keyid:always, keyid:nonss | — | Опция authorityKeyIdentifier: просит включить в AKID subject key identifier эмитента. Суффикс always делает включение обязательным (ошибка, если элемент нельзя включить).⚠️ Без суффикса элемент включается, если возможно; always форсирует включение и приводит к ошибке при недоступности SKID эмитента. |
| authorityKeyIdentifier = …, issuer[:always|:nonss]знач.: issuer, issuer:always, issuer:nonss | — | Опция authorityKeyIdentifier: просит включить в AKID серийный номер и issuer distinguished name (имя ‘дедушки’ относительно субъекта) сертификата эмитента.⚠️ Суффикс always форсирует включение; nonss делает элемент условным при условии, что сертификат не самоподписан. |
| noCheck = ignoredзнач.: любое значение (игнорируется) | — | Расширение OCSP No Check (id-pkix-ocsp-nocheck). Сигнализирует клиентам, что статус отзыва самого сертификата OCSP-responder проверять не нужно, что разрывает рекурсию ‘кто проверит проверяющего’. Документация: ‘This is a string extension. It is parsed, but ignored.’⚠️ OpenSSL вставляет расширение независимо от заданного справа значения; по man-странице используется форма ‘noCheck = ignored’. Поскольку статус responder не проверяется, такой сертификат делают короткоживущим. Обычно некритично. |
| OID 1.3.6.1.5.5.7.48.1.5знач.: 1.3.6.1.5.5.7.48.1.5 | — | OID расширения OCSP No Check, добавляемого ключом noCheck. Содержимое расширения - ASN.1 NULL; смысл несёт само присутствие расширения.⚠️ OID 1.3.6.1.5.5.7.48.1.5 определён в RFC 6960, а не на man-странице x509v3_config. В OpenSSL-конфиге расширение задаётся именно ключом noCheck, а не по числовому OID. |
| authorityInfoAccess = [critical,] access_id;location[, access_id;location…]знач.: access_id из OCSP, caIssuers (и иные методы по OID); location в форме URI:…, email:…, DNS:… и т.п. | — | Расширение Authority Information Access (AIA). Документация: ‘This extension gives details about how to retrieve information that related to the certificate that the CA makes available. The syntax is access_id;location’. Дословные примеры: ‘authorityInfoAccess = OCSP;URI:http://ocsp.example.com/’ и ‘authorityInfoAccess = OCSP;URI:http://ocsp.example.com/,caIssuers;URI:http://myca.example.com/ca.cer’.⚠️ Смежное расширение. В сертификате responder это служебное расширение: метод caIssuers нередко указывают, чтобы клиент мог достроить цепочку до CA. Метод OCSP здесь указывает на сам OCSP-сервис. Не путать: само наличие OCSPSigning/noCheck не зависит от AIA. Обычно некритично. |
| crlDistributionPoints = URI:http://example.com/myca.crl[, URI:…] | crlDistributionPoints = cdp_section (имя секции без @)знач.: список location (URI:…, и др.) либо имя секции с полями fullname, relativename, CRLIssuer, reasons | — | Расширение CRL Distribution Points - указывает, где получать CRL. Простая форма по документации: ‘crlDistributionPoints = URI:http://example.com/myca.crl’. Сложная форма - одно значение с именем секции, содержащей точки распространения с полями fullname, relativename, CRLIssuer и reasons.⚠️ Смежное расширение. Для responder с noCheck CRL responder-а как правило не проверяется, но расширение может присутствовать по политике CA. Обычно некритично. |
| subjectAltName = [critical,] type:value[, type:value…]знач.: email:…, email:copy, URI:…, DNS:…, IP:… (напр. IP:192.168.7.1 или IP:13::17), RID:…, otherName:…, dirName:… | — | Расширение Subject Alternative Name - альтернативные имена субъекта. Дословные примеры из документации: ‘subjectAltName = email:copy, email:my@example.com, URI:http://my.example.com/’, ‘subjectAltName = IP:192.168.7.1’, ‘subjectAltName = IP:13::17’.⚠️ Смежное расширение. Для OCSP-responder обычно не требуется, но может присутствовать (например, при идентификации сервиса по DNS/URI). Значение email:copy копирует e-mail из subject DN. |
| issuerAltName = issuer:copy | issuerAltName = type:value[, …]знач.: issuer:copy; большинство типов как у subjectAltName, КРОМЕ email:copy | — | Расширение Issuer Alternative Name - альтернативные имена эмитента. Документация: расширение ‘supports most of the options of subject alternative name; it does not support email:copy’. Типичная форма: ‘issuerAltName = issuer:copy’ (копирует subjectAltName эмитента).⚠️ Смежное расширение. Служебное расширение; в сертификате responder встречается редко и только по политике CA. |
| certificatePolicies = [critical,] policyOID[, policyOID…] | certificatePolicies = …, @polsectionзнач.: список OID (напр. 1.2.4.5, 1.1.3.4); сложная форма с полями policyIdentifier, CPS.nnn, userNotice.nnn | — | Расширение Certificate Policies. Простая форма по документации: 'certificatePolicies = 1.2.4.5, 1.1.3.4’. Сложная форма использует секцию с полями policyIdentifier, CPS.nnn = value и userNotice.nnn = @notice.⚠️ Смежное расширение. В сертификате responder присутствует, если CA требует указывать политику; на сам механизм делегирования OCSP не влияет. Может быть критичным по политике CA. |
| tlsfeature = name_or_number[, name_or_number…]знач.: status_request, status_request_v2; либо число 0…65535 | — | Расширение TLS Feature (RFC 7633, ‘Must-Staple’). Документация: ‘Each identifier may be a number (0…65535) or a supported name. The supported names are: status_request and status_request_v2.’ Пример: ‘tlsfeature = status_request’.⚠️ Смежное расширение, близкое к теме OCSP: status_request требует от TLS-сервера присылать OCSP-staple (Must-Staple). Это расширение ставят на TLS-сертификат сервера, а не на сам сертификат OCSP-responder; включено как смежный, документированный на странице параметр. |
| <numeric_OID> = [critical,] ASN1:: | <numeric_OID> = [critical,] DER:знач.: ASN1:UTF8String:…, ASN1:BOOLEAN:…, и др.; либо DER:01:02:03:04 (сырые байты в hex) | — | Механизм задания произвольного расширения по числовому OID, не имеющего отдельного ключевого слова. Дословные примеры: ‘1.2.3.4 = critical, ASN1:UTF8String:Some random data’ и ‘1.2.3.4 = DER:01:02:03:04’.⚠️ Смежное расширение. Через эту форму можно вручную вписать любое расширение по OID (в т.ч. редкие). Для id-pkix-ocsp-nocheck в OpenSSL предпочтителен именованный ключ noCheck, но при необходимости расширение можно задать и числовым OID через DER:. |
Версии и подводные камни:
Делегирование OCSP по RFC 6960 действительно только тогда, когда сертификат responder выпущен тем же CA, чьи сертификаты он подтверждает, и содержит extendedKeyUsage = OCSPSigning. Иначе клиенты отвергнут ответы.
Без расширения OCSP No Check клиент может попытаться проверить статус самого сертификата responder, что приводит к рекурсии; альтернатива noCheck - делать сертификат responder короткоживущим.
Расширение noCheck в OpenSSL строковое и фактически игнорирует заданное справа значение (‘It is parsed, but ignored’), поэтому ‘noCheck = ignored’ и любое иное значение справа дают одинаковый результат - пустое расширение id-pkix-ocsp-nocheck.
Документация x509v3_config не задаёт критичность по умолчанию для перечисленных расширений; критичность определяется только наличием префикса critical (‘If critical is present then the extension will be marked as critical’).
OID-ы (OCSPSigning 1.3.6.1.5.5.7.3.9 и id-pkix-ocsp-nocheck 1.3.6.1.5.5.7.48.1.5) взяты из RFC 5280/6960, а не из man-страницы x509v3_config - на самой странице приведены текстовые имена и ключ noCheck.
Имена и формы записи приведены по странице master (docs.openssl.org/master). В старых версиях OpenSSL набор распознаваемых имён EKU и имя contentCommitment мог отличаться - при использовании старой версии сверяйтесь с её man-страницей.
Расширения authorityInfoAccess, crlDistributionPoints, subjectAltName, issuerAltName, certificatePolicies, tlsfeature и форма произвольного расширения по OID добавлены как документированные на странице и потенциально присутствующие в сертификате responder. Часть из них (особенно tlsfeature/status_request) относится прежде всего к TLS-сертификату сервера, а не к самому OCSP-responder, и приведена как смежный параметр - это отмечено в notes соответствующих записей.
5.2. Запускаем responder
Для проверки можно поднять встроенный responder прямо в консоли:
openssl ocsp -port 2560 \ -index /root/ca/PersonIntermediateCA/index.txt \ -CA /root/ca/PersonIntermediateCA/certs/PersonIntermediateCA.cert.pem \ -rsigner /root/ca/PersonIntermediateCA/certs/ocsp.person.cert.pem \ -rkey /root/ca/PersonIntermediateCA/private/ocsp.person.key.pem \ -text
Где -index — база index.txt (именно её наличие переключает команду в режим responder); -CA — сертификат выпустившего CA; -rsigner/-rkey — сертификат и ключ для подписи ответов.
⚠️ Встроенный responder читает
index.txtодин раз при старте и не перечитывает его на лету (исключение — режим-multi, где дочерние процессы сами замечают изменения базы). Значит, после нового отзыва однопоточный responder нужно перезапускать. И вообще встроенныйopenssl ocspрассчитан скорее на тесты и внутренние нужды, чем на высокую нагрузку.
Расширенный справочник: openssl ocsp — режим responder (сервер)
Показать таблицу — 27 опций (openssl ocsp: responder)
Параметр | Синтаксис / значения | По умолч. | Описание и нюансы |
|---|---|---|---|
| -index indexfile | — | Имя текстового индекс-файла в формате ca, содержащего информацию об отзыве сертификатов. Именно наличие этой опции переключает команду в режим responder.⚠️ Серверная сторона. Критично: без -index команда работает как клиент. При указании -index обязательны также -CA и -rsigner. подробное построчное описание столбцов индекс-файла (статусы V/R/E, даты, серийный номер, DN) в самой man-странице openssl-ocsp не приводится – там сказано лишь “text index file in ca format”; формат описан в документации openssl ca. |
| -CA file | — | CA-сертификаты, соответствующие информации об отзыве в индекс-файле, заданном через -index.⚠️ Серверная сторона. Обязателен в режиме responder (при -index). Принимает PEM, DER или PKCS#12. Не путать с -CAfile/-CApath, которые используются для проверки ответа. |
| -rsigner file | — | Сертификат, которым подписываются OCSP-ответы (сертификат OCSP-подписанта).⚠️ Серверная сторона. Обязателен в режиме responder (при -index). Принимает PEM, DER или PKCS#12. Обычно содержит EKU id-kp-OCSPSigning (1.3.6.1.5.5.7.3.9). |
| -rkey file | — | Приватный ключ для подписи OCSP-ответов; если не указан, используется файл, заданный в -rsigner.⚠️ Серверная сторона. Дословно: “if not present the file specified in the -rsigner option is used”. Удобно, когда сертификат и ключ хранятся раздельно. Документированного строкового значения default нет – поведение по умолчанию описано словесно. |
| -passin arg | — | Источник пароля для приватного ключа подписанта (форматы pass:, env:, file: и др. описаны в openssl-passphrase-options).⚠️ Серверная сторона. Нужна, если ключ из -rkey/-rsigner зашифрован. |
| -rother file | — | Дополнительные сертификаты, включаемые в OCSP-ответ (например, цепочка).⚠️ Серверная сторона. Принимает PEM, DER или PKCS#12. Не действует совместно с -resp_no_certs (когда сертификаты вообще не включаются). |
| -rsigopt nm:v | — | Передать параметры алгоритму подписи при подписании OCSP-ответов в виде пар имя:значение. Имена и значения зависят от алгоритма.⚠️ Серверная сторона. Например, rsa_padding_mode:pss и rsa_pss_saltlen:digest для RSA-PSS. |
| -rmd digest | — | Хэш-алгоритм (digest), используемый при подписи ответа.⚠️ Серверная сторона. Дословно: “The digest to use when signing the response”. Не путать с -rcid (digest для идентификации сертификата в CertID). |
| -rcid digest | same digest used in the request | Хэш-алгоритм для идентификации сертификата (certificate identification) в OCSP-ответе. По умолчанию используется тот же digest, что и в запросе.⚠️ Серверная сторона. Дословно: “The default is the same digest algorithm used in the request”. Можно использовать любой digest, поддерживаемый openssl-dgst. Опция относительно новая (ветка OpenSSL 3.x); в 1.0.x/1.1.x отсутствует. |
| -resp_no_certs | — | Не включать никакие сертификаты в OCSP-ответ.⚠️ Серверная сторона. Флаг без аргумента. Уменьшает размер ответа, если клиент уже доверяет подписанту. |
| -resp_key_id | subject name | Идентифицировать сертификат подписанта по идентификатору ключа (key ID); по умолчанию используется имя субъекта (subject name).⚠️ Серверная сторона. Флаг без аргумента. Дословно: “Identify the signer certificate using the key ID, default is to use the subject name”. Влияет на поле responderID (byKey вместо byName). |
| -nmin minutes | — | Число минут, в течение которых актуальна информация об отзыве; используется для поля nextUpdate.⚠️ Серверная сторона. В документации -nmin и -ndays описаны одной формулировкой (“Number of minutes or days…”). Если не заданы ни -nmin, ни -ndays, поле nextUpdate опускается. |
| -ndays days | — | Число дней, в течение которых актуальна информация об отзыве; используется для поля nextUpdate.⚠️ Серверная сторона. Если не заданы ни -ndays, ни -nmin, поле nextUpdate опускается (свежая информация об отзыве доступна немедленно). |
| -port portnum | — | Порт, на котором responder слушает OCSP-запросы. Поддерживаются IPv4 и IPv6. Порт можно задать также через -url. Значение 0 означает автоматический выбор любого доступного порта.⚠️ Серверная сторона. Дословно: “A 0 argument indicates that any available port shall be chosen automatically”. Встроенный сервер предназначен преимущественно для тестирования. |
| -url responder_url | — | URL OCSP-responder. В режиме клиента задаёт адрес сервиса; на стороне responder через URL-форму можно задать порт прослушивания (альтернатива -port).⚠️ Клиентская и серверная сторона. Документация для -port явно указывает: “The port may also be specified using the -url option”. |
| -nrequest number | unlimited | OCSP-сервер завершит работу после получения number запросов.⚠️ Серверная сторона. Дословно: “default unlimited” – без ограничения (работает бесконечно). |
| -multi process-count | — | Запустить указанное число дочерних процессов responder; родительский процесс перезапускает дочерние по мере необходимости. Дочерние процессы отслеживают изменения индекс-файла CA и автоматически перезагружают его.⚠️ Серверная сторона. Дословно: “Child processes will detect changes in the CA index file and automatically reload it”. Доступно только на POSIX-системах. На Windows не работает. |
| -timeout seconds | — | Тайм-аут соединения с OCSP-responder в секундах. На POSIX-системах при работе в роли responder также ограничивает время ожидания запроса от клиента (с момента приёма соединения до полного получения запроса).⚠️ Клиентская и серверная сторона. Серверное поведение (ограничение ожидания запроса) действует только на POSIX-системах. |
| -ignore_err | — | Игнорировать некорректные (malformed) запросы или ответы. В роли клиента – повторить попытку при получении некорректного ответа. В роли responder – продолжать работу, а не завершаться при получении некорректного запроса.⚠️ Клиентская и серверная сторона. Флаг без аргумента. man-страница master описывает обе семантики: клиентскую («retry if a malformed response is received») и серверную («continue running instead of terminating upon receiving a malformed request»). |
| -nonce | — | Добавить расширение OCSP nonce к запросу. Если запрос подаётся через -reqin, nonce обычно не добавляется; -nonce принудительно добавляет nonce.⚠️ Клиентская сторона (создание/отправка запроса). Документация не описывает зеркалирование nonce из запроса в ответ на стороне responder. |
| -no_nonce | — | Отключить добавление OCSP nonce. При создании запроса (через -cert/-serial) nonce добавляется автоматически; -no_nonce это переопределяет.⚠️ Клиентская сторона. Парная к -nonce. Серверное “подавление nonce в ответе” в man-странице не описано. |
| -badsig | — | Испортить подпись ответа перед его записью; полезно для тестирования.⚠️ Серверная сторона. Флаг без аргумента. Только для отладки/тестов – корректный клиент должен отвергать такой ответ. |
| -reqin file | — | Прочитать OCSP-запрос из файла. На стороне responder используется для обработки заранее сформированного запроса (например, при тестировании без сетевого слушателя).⚠️ Серверная и клиентская сторона. Дословно: “Read OCSP request or response file from file. These option are ignored if OCSP request or response creation is implied by other options”. |
| -respout filename | — | Записать DER-кодированный OCSP-ответ в файл. На стороне responder позволяет сохранить сгенерированный ответ в файл.⚠️ Серверная сторона. Дословно: “Write out the DER-encoded OCSP request or response to filename”. |
| -text | — | Вывести текстовую форму OCSP-запроса, ответа или обоих.⚠️ Клиентская и серверная сторона. Дословно: “Print out the text form of the OCSP request, response or both respectively” (общая формулировка для -req_text/-resp_text/-text). |
| -resp_text | — | Вывести текстовую форму OCSP-ответа.⚠️ Клиентская и серверная сторона. Описана общей формулировкой вместе с -req_text и -text (“request, response or both respectively”); -resp_text относится к ответу. |
| -out filename | standard output | Задать имя выходного файла; по умолчанию – стандартный вывод.⚠️ Клиентская и серверная сторона. Дословно: “specify output filename, default is standard output”. |
Версии и подводные камни:
Опции -nonce/-no_nonce согласно man-странице относятся к созданию/отправке запроса (клиентская сторона); серверной семантики «зеркалирования nonce из запроса в ответ» в документации нет.
-ignore_err в man-странице master описан с двумя семантиками: клиентской (повтор при битом ответе) и серверной (продолжение работы при malformed-запросе).
Документация не задаёт фиксированного числового срока валидности по умолчанию: если не заданы ни -nmin, ни -ndays, поле nextUpdate просто опускается.
-multi и серверное поведение -timeout (ограничение ожидания клиентского запроса) доступны только на POSIX-системах; на Windows не работают. При -multi дочерние процессы автоматически перезагружают изменившийся индекс-файл CA.
Опция -rcid появилась в ветке OpenSSL 3.x; в старых версиях (1.0.x/1.1.x) её нет. Сверяйтесь с man-страницей своей версии.
Подробный формат столбцов индекс-файла (V/R/E и т.д.) в man-странице openssl-ocsp не расписан – там сказано только “text index file in ca format”; формат описан в документации openssl ca.
Встроенный responder openssl ocsp предназначен преимущественно для тестирования и небольших задач, а не для высоконагруженного продакшена.
Порт прослушивания responder можно задать двумя способами: -port или через URL-форму в -url; значение порта 0 означает автоматический выбор.
5.3. Спрашиваем статус (клиентская часть OCSP)
В другом терминале:
openssl ocsp \ -CAfile /root/ca/PersonIntermediateCA/certs/ca-chain.cert.pem \ -issuer /root/ca/PersonIntermediateCA/certs/PersonIntermediateCA.cert.pem \ -cert /root/ca/PersonIntermediateCA/certs/User1.cert.pem \ -url http://127.0.0.1:2560 \ -resp_text
-CAfile — доверенная цепочка для проверки подписи ответа; -issuer — издатель проверяемого сертификата; -cert — что проверяем. В ответе ищем строку статуса:
User1.cert.pem: revoked This Update: ... Revocation Time: ... Revocation Reason: keyCompromise (0x1)
Для неотозванного сертификата там будет good. Запрос можно подписать (-signer), добавить/убрать nonce (-nonce/-no_nonce), сохранить сырой запрос/ответ (-reqout/-respout) — всё в справочнике.
Расширенный справочник: openssl ocsp — режим клиента (запрос статуса)
Показать таблицу — 49 опций (openssl ocsp: клиент)
Параметр | Синтаксис / значения | По умолч. | Описание и нюансы |
|---|---|---|---|
| -issuer filename | — | Указывает сертификат текущего издателя (CA). Из него берутся имя и открытый ключ для вычисления CertID запрашиваемых сертификатов.⚠️ Можно указывать несколько раз. КРИТИЧНО: должен предшествовать любым опциям -cert, к которым относится. Вход может быть в формате PEM, DER или PKCS#12. |
| -cert filename | — | Добавляет сертификат из файла filename в запрос. Сертификат издателя берётся из предыдущей опции -issuer.⚠️ Можно использовать многократно для запроса статуса нескольких сертификатов. Издатель берётся из предшествующего -issuer. |
| -serial numзнач.: десятичное целое; шестнадцатеричное при префиксе 0x; отрицательное при префиксе ‘-’ | — | То же, что -cert, но в запрос добавляется сертификат с серийным номером num (а не из файла).⚠️ Требует предшествующего -issuer. |
| -no_certs | — | Не включать никакие сертификаты в подписанный запрос.⚠️ Применяется к подписанному запросу (см. -signer). |
| -signer filename | — | Подписать OCSP-запрос сертификатом, указанным в этой опции (подписанный запрос).⚠️ Подпись запроса нужна только если ответчик её требует; большинство публичных responder-ов её не требуют. Вход может быть в формате PEM, DER или PKCS#12. |
| -signkey filename | — | Закрытый ключ для подписи OCSP-запроса.⚠️ Если -signkey не задан, закрытый ключ читается из того же файла, что и сертификат -signer. |
| -sign_other filename | — | Дополнительные сертификаты, включаемые в подписанный запрос.⚠️ Вход может быть в формате PEM, DER или PKCS#12. |
| -nonce | nonce добавляется по умолчанию при формировании запроса из -cert/-serial | Добавляет расширение OCSP nonce в запрос (защита от replay-атак: ответ должен содержать тот же nonce).⚠️ При подаче запроса через -reqin nonce по умолчанию не добавляется; -nonce форсирует его добавление. |
| -no_nonce | — | Отключает добавление OCSP nonce в запрос (переопределяет автоматическое добавление).⚠️ Нужен при работе с ответчиками, отдающими предсгенерированные (кэшированные) ответы без nonce. |
| -url responder_url | — | Задаёт хост ответчика и, опционально, порт и путь через URL. Поддерживаются HTTP и HTTPS (SSL/TLS).⚠️ Альтернатива комбинации -host/-path. |
| -host host:port | — | Если задан -host, OCSP-запрос отправляется на узел host по порту port. Host может быть доменным именем или IP-адресом.⚠️ IPv6-адрес заключается в квадратные скобки. Используется вместе с -path вместо -url. |
| -path pathname | / | Задаёт HTTP-путь (pathname) для запроса. Эквивалентно -url со схемой http:// и заданными host, port и path.⚠️ По умолчанию ‘/’. Применяется в паре с -host. |
| -header name=value | — | Добавляет HTTP-заголовок name со значением value в отправляемый OCSP-запрос.⚠️ Можно повторять. Синтаксис в man — строго ‘name=value’ (через знак равенства). Часто нужен заголовок Host для виртуальных хостов ответчика. |
| -proxy [http[s]://][userinfo@]host[:port][/path][?query][#fragment] | переменные окружения http_proxy/HTTP_PROXY (без TLS) или https_proxy/HTTPS_PROXY (с TLS) | HTTP(S) прокси-сервер для доступа к OCSP-серверу, если не действует -no_proxy.⚠️ IPv6-хост заключается в квадратные скобки. |
| -no_proxy addresses | переменные окружения no_proxy/NO_PROXY | Список IP-адресов и/или DNS-имён серверов, для которых HTTP(S) прокси не используется.⚠️ Разделители — запятые и/или пробелы. |
| -timeout seconds | — | Таймаут соединения с OCSP-ответчиком в секундах.⚠️ Значение по умолчанию в man не документировано. |
| -reqout file | — | Записывает сформированный OCSP-запрос в DER-кодировке в файл.⚠️ Удобно для офлайн-формирования запроса и отправки иным транспортом. |
| -respout file | — | Записывает полученный OCSP-ответ в DER-кодировке в файл.⚠️ Для сохранения и последующего анализа ответа. |
| -reqin file | — | Читает готовый OCSP-запрос (DER) из файла вместо его формирования из -cert/-serial.⚠️ Игнорируется, если формирование запроса подразумевается другими опциями. При -reqin nonce по умолчанию не добавляется (см. -nonce). |
| -respin file | — | Читает готовый OCSP-ответ (DER) из файла вместо отправки запроса ответчику.⚠️ Позволяет проверять/печатать ранее сохранённый ответ офлайн. Игнорируется, если создание запроса/ответа подразумевается другими опциями. |
| -req_text | — | Печатает текстовое (человекочитаемое) представление OCSP-запроса. |
| -resp_text | — | Печатает текстовое представление OCSP-ответа. Здесь выводится статус сертификата (good/revoked/unknown). |
| -text | — | Печатает текстовое представление запроса, ответа или обоих.⚠️ Эквивалентно -req_text + -resp_text. |
| -out filename | стандартный вывод (stdout) | Задаёт файл для вывода. По умолчанию вывод направляется в стандартный поток (stdout).⚠️ Перенаправляет текстовый/печатный вывод команды. |
| -CAfile file | — | Файл с доверенными CA-сертификатами для построения цепочки доверия при проверке подписи OCSP-ответа.⚠️ Подробности — в openssl-verification-options(1), раздел Trusted Certificate Options. |
| -CApath dir | — | Каталог с доверенными CA-сертификатами (хешированные имена) для проверки ответа.⚠️ См. openssl-verification-options(1). |
| -CAstore uri | — | URI хранилища доверенных CA-сертификатов для проверки ответа.⚠️ См. openssl-verification-options(1). |
| -no-CAfile | — | Не загружать доверенные CA-сертификаты из стандартного файлового расположения OpenSSL.⚠️ Пишется через дефис (-no-CAfile), а не через подчёркивание. |
| -no-CApath | — | Не загружать доверенные CA-сертификаты из стандартного каталога OpenSSL.⚠️ Пишется через дефис. |
| -no-CAstore | — | Не загружать доверенные CA-сертификаты из стандартного хранилища OpenSSL.⚠️ Пишется через дефис. |
| -verify_other file | — | Файл или URI с дополнительными сертификатами, среди которых ищется сертификат, подписавший OCSP-ответ.⚠️ Нужен, когда сертификат подписанта ответа не вложен в сам ответ. Вход может быть в формате PEM, DER или PKCS#12. |
| -trust_other | — | Сертификаты из -verify_other считаются явно доверенными, дополнительные проверки над ними не выполняются.⚠️ Ослабляет проверку: используйте только для заведомо доверенных сертификатов ответчика. |
| -VAfile file | — | Файл или URI с явно доверенными сертификатами ответчика (Validation Authority).⚠️ Эквивалентно одновременному заданию -verify_other и -trust_other. |
| -no_intern | — | Игнорировать сертификаты, вложенные в OCSP-ответ, при поиске сертификата подписанта.⚠️ Заставляет искать подписанта только среди -verify_other/-VAfile. |
| -no_signature_verify | — | Не проверять подпись на OCSP-ответе.⚠️ Допускает недействительные подписи — обычно только для тестирования. |
| -no_cert_verify | — | Вообще не проверять сертификат подписанта OCSP-ответа.⚠️ Позволяет подписать ответ любым сертификатом — только для тестирования. |
| -no_cert_checks | — | Не выполнять дополнительные проверки сертификата подписанта OCSP-ответа.⚠️ Отключает специфичные для OCSP проверки, оставляя базовую проверку цепочки. |
| -no_chain | — | Не использовать сертификаты из ответа как дополнительные недоверенные CA-сертификаты при построении цепочки. |
| -no_explicit | — | Не доверять корневому CA явно, даже если он помечен как доверенный для подписи OCSP. |
| -noverify | — | Не пытаться проверять ни подпись OCSP-ответа, ни значения nonce; также отключает всю проверку сертификата ответчика.⚠️ Полное отключение проверки ответа; обычно только для отладки. |
| -validity_period nsec | 300 (5 минут) | Допустимый диапазон ошибки в секундах при проверке актуальности OCSP-ответа (компенсация рассинхронизации часов между клиентом и ответчиком).⚠️ Документированный default — 5 минут. Слишком малое значение приведёт к ложным отказам при расхождении часов. |
| -status_age age | по умолчанию эта проверка не выполняется | Если в ответе отсутствует поле nextUpdate, проверяется, что возраст поля thisUpdate (notBefore) не старше age секунд.⚠️ Защита от приёма устаревших ответов без явной границы действия. |
| -rcid digest | тот же алгоритм, что использован в запросе | Задаёт алгоритм хеширования для идентификации сертификата (CertID) в OCSP-ОТВЕТЕ.⚠️ Не путать с digest-опцией запроса. Допустим любой digest, поддерживаемый openssl-dgst(1). |
| -sha1 | -sha256 | … (имя digest)знач.: -sha1, -sha224, -sha256, -sha384, -sha512, -md5 и др. | SHA-1 | Задаёт алгоритм хеширования для идентификации сертификата (CertID) в OCSP-запросе. Допустим любой digest, поддерживаемый командой openssl dgst.⚠️ В man это обобщённая digest-опция; буквального флага -dgst НЕТ — алгоритм указывается именем (например -sha256). Может задаваться несколько раз — влияет на последующие идентификаторы сертификатов. КРИТИЧНО: многие responder-ы поддерживают только SHA-1 для CertID и вернут ‘unknown’ при другом digest. |
| -sha1 | значение по умолчанию для CertID | Использовать SHA-1 как алгоритм хеширования для CertID в запросе (частный случай digest-опции).⚠️ Де-факто стандарт для OCSP CertID; совместим с большинством responder-ов. В man отдельно не перечислен — это пример digest-опции. |
| -sha256 | — | Использовать SHA-256 как алгоритм хеширования для CertID (частный случай digest-опции).⚠️ Поддерживается не всеми ответчиками; при несовпадении CertID ответ будет ‘unknown’. В man отдельно не перечислен — пример digest-опции. |
| Cert Status: goodзнач.: good | revoked | unknown | — | Значение статуса в OCSP-ответе: сертификат не числится отозванным на момент ответа (положительный статус).⚠️ Не документировано в man openssl-ocsp(1) напрямую — определено RFC 6960 (CertStatus), печатается в текстовом выводе (-resp_text/-text). ‘good’ не гарантирует, что сертификат был выпущен. |
| Cert Status: revoked | — | Значение статуса: сертификат отозван. Сопровождается временем отзыва (Revocation Time) и, при наличии, причиной (Revocation Reason).⚠️ Источник — RFC 6960, не сам man. Может быть временным (certificateHold) или постоянным. |
| Cert Status: unknown | — | Значение статуса: ответчику неизвестен запрашиваемый сертификат (например, выпущен другим CA или несовпадение CertID).⚠️ Источник — RFC 6960, не сам man. Частая причина — неверный -issuer или несовместимый digest CertID. |
Версии и подводные камни:
Буквальный флаг -dgst в man отсутствует: алгоритм CertID задаётся именем digest (например -sha256); в документации это обобщённая digest-опция, default = SHA-1.
Опции -no_signature_verify, -no_cert_verify, -no_cert_checks, -no_chain, -no_explicit, -noverify, -trust_other ослабляют или отключают проверку ответа — применять только для отладки/тестов, не в продакшене.
Синтаксис -header в текущем man — строго ‘name=value’ (через знак равенства); двухаргументная форма ‘name value’ документацией не подтверждается.
Значения статуса good/revoked/unknown в самом man openssl-ocsp(1) не перечислены — они определены RFC 6960 (CertStatus) и печатаются в текстовом выводе ответа.
Для -timeout значение по умолчанию в man не документировано.
Опции отключения стандартных хранилищ доверия пишутся через дефис (-no-CAfile, -no-CApath, -no-CAstore), тогда как остальные no_*-флаги — через подчёркивание; легко перепутать.
Многие verification-опции (-attime, -crl_check, -purpose, -verify_depth, -x509_strict и др.) и provider-опции (-provider, -propquery) делегированы в openssl-verification-options(1)/openssl(1) и не относятся напрямую к формированию OCSP-запроса.
Шаг 6. Публикуем CRL/OCSP по HTTP и проверяем отзыв на сервере
Сейчас CRL лежит файлом на CA, а responder слушает 127.0.0.1. Но в выпущенных сертификатах прописаны публичные URL http://pki.certservice.info/crl/... и http://ocsp.certservice.info/. Поднимем nginx, который их обслужит.
Публичный nginx — отдельная машина, не ваш Root/Intermediate CA: сам CA в сеть торчать не должен. Для OCSP есть два варианта размещения:
Вариант A — публичный nginx только проксирует OCSP во внутренний responder, живущий в защищённом сегменте:
proxy_pass http://ocsp-internal.certservice.lan:2560;Вариант B — responder работает прямо на публичном PKI-хосте. Тогда на этот хост копируют только: реплику
index.txt, сертификат OCSP-responder’а и его приватный ключ.Приватный ключ Intermediate CA на публичный хост не копируется никогда. И учтите: ключ responder’а — это тоже онлайн-ключ (лежит на сетевой машине), но он подписывает лишь OCSP-ответы, а не сертификаты, поэтому его компрометация ≠ компрометация CA. Всё равно держите его в защищённом сегменте, а сертификат responder’а — короткоживущим.
6.1. CRL раздают в DER
Браузеры и многие клиенты ждут CRL в DER. Конвертируем (имя файла должно совпадать с тем, что в crlDistributionPoints — у нас PersonIntermediateCA.crl):
openssl crl \ -inform PEM -in /root/ca/PersonIntermediateCA/crl/PersonIntermediateCA.crl.pem \ -outform DER -out /var/www/pki/crl/PersonIntermediateCA.crl
6.2. nginx: раздача CRL и прокси к OCSP
server { listen 80; server_name pki.certservice.info; root /var/www/pki; location /crl/ { autoindex on; } location /certs/ { autoindex on; } } server { listen 80; server_name ocsp.certservice.info; location / { proxy_pass http://127.0.0.1:2560; # встроенный openssl ocsp responder принимает POST с DER-телом proxy_set_header Host $host; } }
6.3. Responder под systemd
Пути ниже даны для Варианта B (responder на PKI-хосте). Для Варианта A (внутренний responder) поправьте их на каталог, куда вы скопировали index.txt, сертификат и ключ responder’а, — весь Intermediate CA на эту машину переносить не нужно.
/etc/systemd/system/ocsp-person.service:
systemd-юнит ocsp-person.service (развернуть)
[Unit] Description=OpenSSL OCSP responder (Person Intermediate CA) After=network.target [Service] ExecStart=/usr/bin/openssl ocsp -port 2560 \ -index /root/ca/PersonIntermediateCA/index.txt \ -CA /root/ca/PersonIntermediateCA/certs/PersonIntermediateCA.cert.pem \ -rsigner /root/ca/PersonIntermediateCA/certs/ocsp.person.cert.pem \ -rkey /root/ca/PersonIntermediateCA/private/ocsp.person.key.pem Restart=always RestartSec=2 [Install] WantedBy=multi-user.target
systemctl daemon-reload systemctl enable --now ocsp-person.service # после каждого отзыва — подхватить обновлённый index.txt: systemctl restart ocsp-person.service
6.4. Автоматизируем регенерацию CRL
У CRL есть nextUpdate (default_crl_days = 30). Если CRL «протухнет», строгие клиенты сочтут проверку отзыва неуспешной (ошибка 12). Регенерируем по cron. Скрипт /root/ca/bin/regen-crl.sh:
Скрипт regen-crl.sh и запись в cron (развернуть)
#!/usr/bin/env bash set -euo pipefail CFG=/root/ca/config/PersonIntermediateCA.cnf CRL_PEM=/root/ca/PersonIntermediateCA/crl/PersonIntermediateCA.crl.pem CRL_DER=/var/www/pki/crl/PersonIntermediateCA.crl openssl ca -config "$CFG" -gencrl -out "$CRL_PEM" openssl crl -inform PEM -in "$CRL_PEM" -outform DER -out "$CRL_DER" # rsync -a "$CRL_DER" pki@pki.certservice.info:/var/www/pki/crl/
0 1 * * * /root/ca/bin/regen-crl.sh >> /var/log/ca/regen-crl.log 2>&1
6.5. Проверка отзыва и OCSP stapling на стороне веб-сервера
До сих пор речь шла о раздаче статуса. Но веб-сервер и сам — потребитель отзыва, причём в двух ролях:
OCSP stapling — сервер заранее получает OCSP-ответ для своего собственного сертификата и «пристёгивает» его к TLS-рукопожатию, избавляя браузер от похода к responder’у. В nginx это семейство
ssl_stapling*, в Apache —SSLUseStapling/SSLStapling*.Проверка отзыва клиентских сертификатов (ради чего вся серия) — когда сервер требует клиентский сертификат, он должен проверять его отзыв по CRL (
ssl_crl/SSLCARevocation*) или по OCSP (ssl_ocsp/SSLOCSPEnable).
Минимальный stapling для серверного сертификата в nginx:
ssl_stapling on; ssl_stapling_verify on; ssl_trusted_certificate /etc/ssl/certs/ca-chain.cert.pem; # издатель + корень resolver 127.0.0.1 valid=300s; # без resolver stapling молча не работает
Самая частая причина «молчаливого» отказа stapling в nginx — забытый resolver: без него nginx не может разрешить имя responder’а из AIA. Проверку отзыва клиентских сертификатов мы детально разберём в Части 3, но все директивы — уже в справочнике ниже.
В справочнике директивы делятся на три группы, которые легко перепутать:
ssl_stapling*/SSLStapling*— OCSP-ответ для своего серверного сертификата (stapling);ssl_crl,ssl_ocspиSSLCARevocation*,SSLOCSP*— отзыв клиентских сертификатов пользователей (mTLS);SSLProxyCARevocation*— отдельная история: проверка отзыва сертификатов upstream-серверов, когда Apache работает SSL-прокси; к пользовательским сертификатам отношения не имеет.
Расширенный справочник: nginx и Apache — OCSP stapling и проверка отзыва
Для каждой директивы — синтаксис, значения, умолчание, контекст и версия, в которой она появилась.
Показать таблицу — 38 директив (nginx и Apache)
Параметр | Синтаксис / значения | По умолч. | Описание и нюансы |
|---|---|---|---|
| ssl_stapling on | off;знач.: on | off | ssl_stapling off; | Включает или выключает stapling OCSP-ответов сервером для собственного серверного сертификата (RFC 6066, раздел 8).⚠️ NGINX, контекст: http, server. Появилась в 1.3.7. Чтобы stapling работал, должен быть известен сертификат издателя серверного сертификата: если он отсутствует среди промежуточных в ssl_certificate, его надо положить в ssl_trusted_certificate. Для разрешения имени OCSP-респондера нужна директива resolver (из ngx_http_core_module), иначе онлайн-запросы к респондеру не выполняются (типичная ошибка). |
| ssl_stapling_verify on | off;знач.: on | off | ssl_stapling_verify off; | Включает или выключает проверку (верификацию) OCSP-ответов сервером.⚠️ NGINX, контекст: http, server. Появилась в 1.3.7. Для проверки в ssl_trusted_certificate должны быть указаны сертификат издателя серверного сертификата, корневой и все промежуточные сертификаты. По умолчанию выключена; при включении неполная цепочка доверия приводит к ошибке проверки ответа. |
| ssl_stapling_file file;знач.: путь к файлу (DER-формат) | — | При установке stapled OCSP-ответ берётся из указанного файла вместо запроса к OCSP-респондеру, указанному в серверном сертификате.⚠️ NGINX, контекст: http, server. Появилась в 1.3.7. Файл должен быть в формате DER, как его выдаёт команда openssl ocsp. Полезно при недоступности респондера из сети сервера; требует периодического обновления файла вручную/по cron, иначе ответ протухнет. |
| ssl_stapling_responder url;знач.: URL вида http://… | — | Переопределяет URL OCSP-респондера, указанный в расширении сертификата Authority Information Access (AIA).⚠️ NGINX, контекст: http, server. Появилась в 1.3.7. Поддерживаются только респондеры по http:// (https:// не поддерживается). Применяется именно к stapling серверного сертификата. |
| ssl_trusted_certificate file;знач.: путь к файлу (PEM) | — | Задаёт файл с доверенными CA-сертификатами в формате PEM, используемыми для проверки клиентских сертификатов и OCSP-ответов (если включён ssl_stapling).⚠️ NGINX, контекст: http, server. Появилась в 1.3.7. В отличие от ssl_client_certificate, список этих сертификатов не отправляется клиентам. Ключевая директива для работы ssl_stapling_verify и для OCSP-валидации клиента (ssl_ocsp). |
| ssl_client_certificate file;знач.: путь к файлу (PEM) | — | Задаёт файл с доверенными CA-сертификатами в формате PEM, используемыми для проверки клиентских сертификатов и OCSP-ответов. Список этих сертификатов отправляется клиентам.⚠️ NGINX, контекст: http, server. Ключевое отличие от ssl_trusted_certificate: имена CA из этого файла рассылаются клиентам в ходе рукопожатия (Certificate Request), что может раскрывать список доверенных CA. Используется совместно с ssl_verify_client для mutual TLS; задействуется и при OCSP-валидации клиента. |
| ssl_crl file;знач.: путь к файлу (PEM) | — | Задаёт файл со списком отозванных сертификатов (CRL) в формате PEM, используемый для проверки клиентских сертификатов.⚠️ NGINX, контекст: http, server. Появилась в 0.8.7. При использовании промежуточных сертификатов их CRL должны быть указаны в том же файле. Относится к проверке клиентских сертификатов (mutual TLS), не к серверному сертификату. |
| ssl_verify_client on | off | optional | optional_no_ca;знач.: on | off | optional | optional_no_ca | ssl_verify_client off; | Включает проверку клиентских сертификатов; результат сохраняется в переменной $ssl_client_verify.⚠️ NGINX, контекст: http, server. Параметр optional (с 0.8.7) запрашивает и проверяет сертификат, если он предъявлен. Параметр optional_no_ca (с 1.3.8 и 1.2.5) запрашивает сертификат, но не требует подписи доверенным CA (для внешней проверки). Для работы ssl_ocsp эта директива должна быть on или optional. |
| ssl_ocsp on | off | leaf;знач.: on | off | leaf | ssl_ocsp off; | Включает OCSP-валидацию цепочки клиентского сертификата. Параметр leaf включает проверку только самого клиентского сертификата (конечного).⚠️ NGINX, контекст: http, server. Появилась в 1.19.0. Требует ssl_verify_client on или optional. Для разрешения имени OCSP-респондера нужна директива resolver. Это проверка отзыва клиентских сертификатов через OCSP (не stapling серверного). |
| ssl_ocsp_cache off | [shared:name:size];знач.: off | shared:name:size | ssl_ocsp_cache off; | Задаёт имя и размер кэша, хранящего статус клиентских сертификатов при OCSP-валидации. Кэш разделяется между всеми рабочими процессами.⚠️ NGINX, контекст: http, server. Появилась в 1.19.0. Кэш с одним именем можно использовать в нескольких виртуальных серверах. Параметр off запрещает использование кэша. |
| ssl_ocsp_responder url;знач.: URL вида http://… | — | Переопределяет URL OCSP-респондера из расширения AIA для валидации (ssl_ocsp) клиентских сертификатов.⚠️ NGINX, контекст: http, server. Появилась в 1.19.0. Поддерживаются только http:// респондеры. Не путать с ssl_stapling_responder (тот для серверного сертификата). |
| ssl_verify_depth number;знач.: целое число | ssl_verify_depth 1; | Задаёт глубину проверки в цепочке клиентских сертификатов.⚠️ NGINX, контекст: http, server. По умолчанию 1. При длинных цепочках (несколько промежуточных CA) значение по умолчанию может быть мало и проверку придётся увеличить. |
| SSLUseStapling on|offзнач.: on | off | SSLUseStapling off | Включает stapling OCSP-ответов через TLS-расширение certificate status request: сервер кэширует OCSP-ответы и предъявляет их клиентам в TLS-рукопожатии.⚠️ Apache mod_ssl, контекст: server config, virtual host. Доступна с httpd 2.4.8. Требует настроенного SSLStaplingCache. Status: Extension. |
| SSLStaplingCache typeзнач.: тип кэша (как у SSLSessionCache), напр. shmcb:/path(size), dbm:/path | SSLStaplingCache none | Настраивает тип и расположение backend-кэша для хранения OCSP-ответов stapling. Типы те же, что у SSLSessionCache.⚠️ Apache mod_ssl, контекст только server config (не virtual host). Доступна с httpd 2.4.8. Обязательна для работы SSLUseStapling. Документированное значение по умолчанию — none. Типичный вариант shmcb (shared memory circular buffer). Status: Extension. |
| SSLStaplingResponderTimeout secondsзнач.: целое число секунд | SSLStaplingResponderTimeout 10 | Задаёт таймаут запросов к OCSP-респондеру при включённом SSLUseStapling.⚠️ Apache mod_ssl, контекст: server config, virtual host. Доступна с httpd 2.4.8. Status: Extension. |
| SSLStaplingReturnResponderErrors on|offзнач.: on | off | SSLStaplingReturnResponderErrors on | При включении ответы об ошибке от OCSP-респондера передаются (staple) клиенту; при off сервер не возвращает клиенту ошибочный ответ респондера.⚠️ Apache mod_ssl, контекст: server config, virtual host. Доступна с httpd 2.4.8. Документированное значение по умолчанию — on. Status: Extension. |
| SSLStaplingFakeTryLater on|offзнач.: on | off | SSLStaplingFakeTryLater on | Если включено и не удалось получить OCSP-ответ от респондера, клиенту синтетически прикрепляется ответ tryLater.⚠️ Apache mod_ssl, контекст: server config, virtual host. Доступна с httpd 2.4.8. Status: Extension. |
| SSLStaplingStandardCacheTimeout secondsзнач.: целое число секунд | SSLStaplingStandardCacheTimeout 3600 | Задаёт время хранения в кэше успешных (valid) OCSP-ответов для stapling до их обновления у респондера.⚠️ Apache mod_ssl, контекст: server config, virtual host. Доступна с httpd 2.4.8. По умолчанию 3600 секунд (1 час). Status: Extension. |
| SSLStaplingErrorCacheTimeout secondsзнач.: целое число секунд | SSLStaplingErrorCacheTimeout 600 | Задаёт время хранения в кэше ошибочных (недействительных) OCSP-ответов для stapling.⚠️ Apache mod_ssl, контекст: server config, virtual host. Доступна с httpd 2.4.8. По умолчанию 600 секунд. Status: Extension. |
| SSLStaplingForceURL uriзнач.: URI OCSP-респондера | — | Принудительно использует указанный URI OCSP-респондера для stapling вместо URI из расширения AIA сертификата.⚠️ Apache mod_ssl, контекст: server config, virtual host. Доступна с httpd 2.4.8. Значение по умолчанию не документировано. Status: Extension. |
| SSLStaplingResponseMaxAge secondsзнач.: целое число секунд; -1 — без ограничения | SSLStaplingResponseMaxAge -1 | Задаёт максимально допустимый возраст (свежесть) OCSP-ответов, прикрепляемых через stapling. Значение по умолчанию -1 не накладывает ограничения по возрасту.⚠️ Apache mod_ssl, контекст: server config, virtual host. Доступна с httpd 2.4.8. Status: Extension. Не путать с одноимённой по смыслу SSLOCSPResponseMaxAge — эта директива относится именно к stapling серверного сертификата. |
| SSLStaplingResponseTimeSkew secondsзнач.: целое число секунд | SSLStaplingResponseTimeSkew 300 | Задаёт максимально допустимый рассинхрон времени (time skew) при проверке полей thisUpdate и nextUpdate OCSP-ответа для stapling.⚠️ Apache mod_ssl, контекст: server config, virtual host. Доступна с httpd 2.4.8. По умолчанию 300 секунд. Status: Extension. Аналог SSLOCSPResponseTimeSkew, но для stapling серверного сертификата. |
| SSLOCSPEnable on|leaf|off [flags]знач.: on | leaf | off; flag: no_ocsp_for_cert_ok | SSLOCSPEnable off | Включает OCSP-валидацию цепочки клиентского сертификата. on — проверяются сертификаты цепочки клиента после обычной верификации (включая CRL-проверки); leaf — проверяется только конечный (клиентский) сертификат.⚠️ Apache mod_ssl, контекст: server config, virtual host. Status: Extension. Режим leaf доступен с httpd 2.4.34; флаг no_ocsp_for_cert_ok доступен с 2.4.29 и разрешает проходить сертификатам без URL OCSP-респондера. Это проверка клиентских сертификатов. |
| SSLOCSPDefaultResponder uriзнач.: URI OCSP-респондера | — | Задаёт OCSP-респондер по умолчанию. Если SSLOCSPOverrideResponder не включён, заданный URI используется, только если в проверяемом сертификате не указан респондер.⚠️ Apache mod_ssl, контекст: server config, virtual host. Status: Extension. Относится к валидации клиентских сертификатов (SSLOCSPEnable). |
| SSLOCSPOverrideResponder on|offзнач.: on | off | SSLOCSPOverrideResponder off | Принудительно использует заданный SSLOCSPDefaultResponder при OCSP-валидации, переопределяя URI респондера из сертификата.⚠️ Apache mod_ssl, контекст: server config, virtual host. Status: Extension. |
| SSLOCSPNoverify on|offзнач.: on | off | SSLOCSPNoverify off | Пропускает (отключает) проверку сертификата самого OCSP-респондера. Полезно при тестировании, когда ответ респондера подписан недоверенным сертификатом.⚠️ Apache mod_ssl, контекст: server config, virtual host. Status: Extension. Доступна с httpd 2.4.26 при использовании OpenSSL 0.9.7 и новее. Небезопасно в проде: отключение проверки подписи респондера снижает доверие к OCSP-ответу. |
| SSLOCSPResponderCertificateFile fileзнач.: путь к файлу (PEM, доверенные сертификаты респондеров) | — | Задаёт файл с доверенными PEM-кодированными сертификатами OCSP-респондеров, используемыми для проверки подписи OCSP-ответов.⚠️ Apache mod_ssl, контекст: server config, virtual host. Status: Extension. Доступна с httpd 2.4.26 при использовании OpenSSL 0.9.7 и новее. Применяется, когда сертификат респондера не входит в обычную доверенную цепочку. |
| SSLOCSPProxyURL urlзнач.: URL HTTP-прокси | — | Задаёт URL HTTP-прокси, через который выполняются все запросы к OCSP-респондерам.⚠️ Apache mod_ssl, контекст: server config, virtual host. Status: Extension. Доступна с httpd 2.4.19. Полезно, когда у сервера нет прямого доступа к OCSP-респондерам и требуется выход через корпоративный прокси. |
| SSLOCSPResponderTimeout secondsзнач.: целое число секунд | SSLOCSPResponderTimeout 10 | Задаёт таймаут запросов к OCSP-респондерам при включённом SSLOCSPEnable.⚠️ Apache mod_ssl, контекст: server config, virtual host. Status: Extension. |
| SSLOCSPResponseMaxAge secondsзнач.: целое число секунд; -1 — без ограничения | SSLOCSPResponseMaxAge -1 | Задаёт максимально допустимый возраст (свежесть) OCSP-ответов. Значение по умолчанию -1 не накладывает ограничения; ответ действителен, пока его поле nextUpdate в будущем.⚠️ Apache mod_ssl, контекст: server config, virtual host. Status: Extension. |
| SSLOCSPResponseTimeSkew secondsзнач.: целое число секунд | SSLOCSPResponseTimeSkew 300 | Задаёт максимально допустимый рассинхрон времени (time skew) при проверке полей thisUpdate и nextUpdate OCSP-ответа.⚠️ Apache mod_ssl, контекст: server config, virtual host. Status: Extension. По умолчанию 300 секунд. |
| SSLOCSPUseRequestNonce on|offзнач.: on | off | SSLOCSPUseRequestNonce on | Определяет, включать ли nonce в запросы к OCSP-респондеру; по умолчанию nonce всегда добавляется и сверяется с ответом.⚠️ Apache mod_ssl, контекст: server config, virtual host. Status: Extension. Доступна с httpd 2.4.10. Отключайте, если респондер не поддерживает nonce (например, Microsoft OCSP Responder), иначе валидация будет падать. |
| SSLCARevocationFile file-pathзнач.: путь к файлу (конкатенированные PEM CRL) | — | Задаёт единый all-in-one файл с конкатенированными PEM-кодированными списками отзыва (CRL) удостоверяющих центров для аутентификации клиентов.⚠️ Apache mod_ssl, контекст: server config, virtual host. Status: Extension. Можно использовать вместо или вместе с SSLCARevocationPath. Читается при старте сервера, пока он работает от root (до сброса привилегий); не перечитывается во время работы — изменения требуют перезапуска сервера. |
| SSLCARevocationPath directory-pathзнач.: путь к каталогу | — | Задаёт каталог с PEM-кодированными CRL удостоверяющих центров для аутентификации клиентов.⚠️ Apache mod_ssl, контекст: server config, virtual host. Status: Extension. Файлы должны быть PEM-кодированы и доступны через hash-имена; нужно создавать симлинки вида hash-value.rN. Читаются при старте сервера от root (до сброса привилегий); не перечитываются во время работы — изменения требуют перезапуска. |
| SSLCARevocationCheck chain|leaf|none [flags …]знач.: chain | leaf | none; flag: no_crl_for_cert_ok | SSLCARevocationCheck none | Включает проверку отзыва на основе CRL. chain (рекомендуется) — проверка CRL для всех сертификатов цепочки; leaf — только для конечного сертификата; none — без проверки.⚠️ Apache mod_ssl, контекст: server config, virtual host. Status: Extension. Требует настроенного хотя бы одного из SSLCARevocationFile/SSLCARevocationPath. Опциональные флаги доступны с httpd 2.4.21. По умолчанию (с 2.3.15) при chain/leaf CRL должны присутствовать, иначе ошибка unable to get certificate CRL; флаг no_crl_for_cert_ok восстанавливает прежнее поведение (совместимость с конфигами 2.2). Типичная ошибка: включить chain, но не предоставить CRL для всех уровней цепочки. |
| SSLProxyCARevocationCheck chain|leaf|noneзнач.: chain | leaf | none | SSLProxyCARevocationCheck none | Включает проверку отзыва по CRL для сертификатов удалённых (upstream) серверов, когда Apache работает как обратный/прямой SSL-прокси. chain — проверка всей цепочки, leaf — только конечного сертификата, none — без проверки.⚠️ Apache mod_ssl, контекст: server config, virtual host, а также proxy section (с httpd 2.4.30). Status: Extension. Требует настроенного хотя бы одного из SSLProxyCARevocationFile/SSLProxyCARevocationPath. При chain/leaf CRL должны быть доступны, иначе ошибка unable to get certificate CRL. |
| SSLProxyCARevocationFile file-pathзнач.: путь к файлу (конкатенированные PEM CRL) | — | Задаёт единый all-in-one файл с конкатенированными PEM-кодированными CRL удостоверяющих центров для аутентификации удалённых серверов (прокси-соединения).⚠️ Apache mod_ssl, контекст: server config, virtual host, а также proxy section (с httpd 2.4.30). Status: Extension. Аналог SSLCARevocationFile, но для проверки сертификатов upstream-серверов. |
| SSLProxyCARevocationPath directory-pathзнач.: путь к каталогу | — | Задаёт каталог с PEM-кодированными CRL удостоверяющих центров для аутентификации удалённых серверов (прокси-соединения). Файлы доступны через hash-имена (симлинки hash-value.rN).⚠️ Apache mod_ssl, контекст: server config, virtual host, а также proxy section (с httpd 2.4.30). Status: Extension. Аналог SSLCARevocationPath, но для проверки сертификатов upstream-серверов. |
Версии и подводные камни:
NGINX: для ssl_stapling и ssl_ocsp обязательна директива resolver (из ngx_http_core_module) для разрешения имени OCSP-респондера; без неё онлайн-запросы к респондеру не выполняются — частая причина молчаливого отказа stapling.
Разделяйте два разных механизма: ssl_stapling*/SSLStapling* относятся к OCSP-ответу для собственного серверного сертификата; ssl_ocsp*/SSLOCSP* и ssl_crl/SSLCARevocation* относятся к проверке отзыва клиентских сертификатов (mutual TLS); SSLProxyCARevocation* — к проверке отзыва сертификатов upstream-серверов при работе Apache в роли SSL-прокси.
NGINX: ssl_client_certificate и ssl_trusted_certificate оба используются для проверки клиентских сертификатов и OCSP-ответов, но список из ssl_client_certificate отправляется клиентам (раскрывая доверенные CA), а из ssl_trusted_certificate — нет. Для stapling серверного сертификата издатель должен быть доступен через ssl_trusted_certificate.
Apache: SSLStaplingCache имеет контекст только server config (глобально), в отличие от остальных Stapling-директив, допустимых также в virtual host; документированное значение по умолчанию — SSLStaplingCache none.
Apache: SSLCARevocationFile/SSLCARevocationPath (а также Proxy-аналоги) читаются на старте сервера от root (до сброса привилегий) и не перечитываются во время работы — при изменении CRL необходим перезапуск сервера, недостаточно простого reload.
Версии NGINX: stapling — с 1.3.7; OCSP-валидация клиента (ssl_ocsp/ssl_ocsp_cache/ssl_ocsp_responder) — с 1.19.0; ssl_crl — с 0.8.7. Версии Apache: stapling (все SSLStapling*) — с 2.4.8; SSLOCSPEnable leaf — с 2.4.34; флаг no_ocsp_for_cert_ok — с 2.4.29; SSLOCSPUseRequestNonce — с 2.4.10; SSLOCSPProxyURL — с 2.4.19; SSLOCSPNoverify и SSLOCSPResponderCertificateFile — с 2.4.26 (OpenSSL 0.9.7+); контекст proxy section у SSLProxyCARevocation* — с 2.4.30; опциональные флаги SSLCARevocationCheck — с 2.4.21.
OCSP-респондеры в nginx (ssl_stapling_responder, ssl_ocsp_responder) поддерживают только схему http:// — https:// не поддерживается. В Apache для выхода к респондерам через прокси используйте SSLOCSPProxyURL.
Apache: SSLOCSPNoverify отключает проверку сертификата самого OCSP-респондера — применять только для тестирования, в продакшене это ослабляет доверие к OCSP-ответам.
Итог
Теперь у нас есть полный жизненный цикл сертификата: выпуск (Часть 1) и отзыв с публикацией статуса (эта часть). Мы:
добавили в сертификаты
crlDistributionPointsиauthorityInfoAccess;научились отзывать сертификат с указанием причины и читать базу
index.txt;генерируем и раздаём CRL (в DER, с авторегенерацией по cron) и разбираем его
openssl crl;проверяем отзыв через
openssl verifyи понимаем коды ошибок;подняли OCSP-responder на отдельном сертификате с
OCSPSigning+noCheckи проксируем его через nginx;знаем все директивы nginx/apache для stapling и проверки отзыва.
В третьей части перейдём к тому, ради чего всё затевалось, — авторизации пользователей по клиентским сертификатам в nginx и apache: ssl_verify_client, проверка по CRL/OCSP на стороне сервера и передача полей сертификата в приложение. Тогда «беспарольный вход в админку» из вступления первой части наконец заработает.
Повторяете у себя — не забудьте проделать то же (CRL + OCSP) для
ServerIntermediateCAиCodeIntermediateCA, поменяв в путях и URLPersonнаServer/Code. Логика идентична, отличаются только имена файлов и хостов.
Источники
Всё в статье сверено с официальной документацией; реферальных и сокращённых ссылок нет.
Документация OpenSSL (ветка master):
openssl-ca — отзыв сертификатов, генерация CRL, ключи секции
[ CA_default ]openssl-crl — просмотр, конвертация и проверка CRL
openssl-ocsp — OCSP-responder и клиентские запросы
openssl-verify и openssl-verification-options — построение цепочки и проверка отзыва
x509v3_config — расширения X.509v3:
crlDistributionPoints,authorityInfoAccess,issuingDistributionPoint, key identifiers,noCheckconfig — формат конфигурационного файла OpenSSL
Веб-серверы:
nginx — ngx_http_ssl_module —
ssl_stapling*,ssl_ocsp*,ssl_crl,ssl_verify_clientApache — mod_ssl —
SSLUseStapling,SSLStapling*,SSLOCSP*,SSLCARevocation*
Стандарты (RFC):
RFC 5280 — профиль X.509-сертификатов и CRL (в т.ч. CDP, AIA, reason codes, критичность расширений)
RFC 6960 — Online Certificate Status Protocol (OCSP);
OCSPSigning,id-pkix-ocsp-nocheckRFC 6066 — TLS Extensions, в т.ч. Certificate Status Request (OCSP stapling)
RFC 7633 — TLS Feature Extension (Must-Staple,
status_request)
Прочее:
OpenSSL PKI Tutorial — CA database — формат файла
index.txt(вmanOpenSSL он не специфицирован)Часть 1. Создаём Root & Intermediate Certificate Authority — предыдущая статья цикла
