Продолжение первой части, где мы развернули двухуровневую инфраструктуру: 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 тоже менялись между ветками — проверяйте их по заголовку своей версии.

В этой части:

  1. Чем отозванный сертификат отличается от просроченного и зачем тут два механизма — CRL и OCSP.

  2. Добавляем в конфиги точки распространения (CDP) и AIA, чтобы выпускаемые сертификаты сами «говорили», где их проверять.

  3. Отзываем сертификат и работаем с базой CA.

  4. Генерируем CRL и разбираем его командой openssl crl.

  5. Проверяем отзыв через openssl verify.

  6. Поднимаем OCSP-responder: выпускаем для него сертификат, запускаем демон, проверяем статус.

  7. Публикуем 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

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

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

relativename = section (значение - фрагмент DN, задаётся как DN-секция)знач.: фрагмент distinguished name (RDN), задаётся через секцию в формате DN

Поле секции DistributionPoint: имя точки распространения, заданное как фрагмент distinguished name; устанавливается как значение поля nameRelativeToCRLIssuer.⚠️ Значение берётся как фрагмент DN (формат как у distinguished name). Взаимоисключаемо с fullname.

CRLIssuer

CRLIssuer = dirName:issuer_sectзнач.: GeneralName (обычно dirName:секция) в формате subject alternative name

Поле секции DistributionPoint: имя издателя CRL, если CRL подписывается не самим издателем сертификата (indirect CRL). Значение в том же формате, что и subject alternative name.⚠️ В разных версиях документации OpenSSL встречается разный регистр имени поля (CRLIssuer и CRLissuer); после выпуска проверяйте openssl x509 -text, что CRL Issuer попал в расширение. Применяется для indirect CRL, часто в связке с dirName: на секцию с C/O/CN.

reasons

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 в этот список не входит.

keyCompromise

reasons = keyCompromise

Значение поля reasons / onlysomereasons: компрометация закрытого ключа субъекта.⚠️ Одно из распознаваемых значений reason flags. Регистр важен.

CACompromise

reasons = CACompromise

Значение поля reasons / onlysomereasons: компрометация ключа удостоверяющего центра (CA).⚠️ Пишется с заглавными CA: CACompromise.

affiliationChanged

reasons = affiliationChanged

Значение поля reasons / onlysomereasons: изменение принадлежности субъекта (например, смена организации).⚠️ Одно из распознаваемых значений reason flags.

superseded

reasons = superseded

Значение поля reasons / onlysomereasons: сертификат заменён новым.⚠️ Одно из распознаваемых значений reason flags.

cessationOfOperation

reasons = cessationOfOperation

Значение поля reasons / onlysomereasons: прекращение деятельности/использования сертификата.⚠️ Одно из распознаваемых значений reason flags.

certificateHold

reasons = certificateHold

Значение поля reasons / onlysomereasons: временная приостановка действия сертификата (hold).⚠️ Одно из распознаваемых значений reason flags.

privilegeWithdrawn

reasons = privilegeWithdrawn

Значение поля reasons / onlysomereasons: отзыв привилегий/полномочий субъекта.⚠️ Одно из распознаваемых значений reason flags.

AACompromise

reasons = AACompromise

Значение поля reasons / onlysomereasons: компрометация Attribute Authority (для атрибутных сертификатов).⚠️ Пишется AACompromise (две заглавные A).

authorityInfoAccess

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

OCSP;URI:http://ocsp.example.com/

access_id в authorityInfoAccess: адрес OCSP-ответчика для проверки статуса сертификата в реальном времени.⚠️ Одно из известных имён метода доступа. После имени - ‘;’ и location (обычно URI:).

caIssuers

caIssuers;URI:http://myca.example.com/ca.cer

access_id в authorityInfoAccess: адрес, по которому можно загрузить сертификат(ы) издателя (CA) для достройки цепочки.⚠️ Часто указывает на DER-сертификат (.cer/.crt). Пишется caIssuers.

ad_timestamping

ad_timestamping;URI:…

access_id в authorityInfoAccess: метод доступа к службе меток времени (Time Stamping).⚠️ Одно из известных имён метода доступа, распознаваемых OpenSSL (подтверждено man-страницей).

AD_DVCS

AD_DVCS;URI:…

access_id в authorityInfoAccess: метод доступа Data Validation and Certification Server (DVCS).⚠️ Известное имя метода доступа (подтверждено man-страницей).

caRepository

caRepository;URI:…

access_id (известное имя метода доступа, семантически относится к Subject Information Access): URI репозитория CA.⚠️ Распознаётся OpenSSL как известное имя метода доступа (подтверждено man-страницей). Семантически принадлежит subjectInfoAccess.

issuingDistributionPoint

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

onlysomereasons = keyCompromise, CACompromiseзнач.: те же значения, что и reasons (keyCompromise, CACompromise, affiliationChanged, superseded, cessationOfOperation, certificateHold, privilegeWithdrawn, AACompromise)

Поле секции issuingDistributionPoint: ограничивает CRL только указанными причинами отзыва (onlySomeReasons).⚠️ Аналог поля reasons, но в контексте IDP. Перечисление через запятую.

onlyuser

onlyuser = TRUEзнач.: boolean (TRUE | FALSE)

Поле секции issuingDistributionPoint: CRL содержит только отзывы сертификатов конечных пользователей (onlyContainsUserCerts).⚠️ Согласно man-странице, значение каждого из onlyuser/onlyCA/onlyAA/indirectCRL - булево.

onlyCA

onlyCA = TRUEзнач.: boolean (TRUE | FALSE)

Поле секции issuingDistributionPoint: CRL содержит только отзывы сертификатов CA (onlyContainsCACerts).⚠️ Булево значение.

onlyAA

onlyAA = TRUEзнач.: boolean (TRUE | FALSE)

Поле секции issuingDistributionPoint: CRL содержит только отзывы атрибутных сертификатов (onlyContainsAttributeCerts).⚠️ Булево значение.

indirectCRL

indirectCRL = TRUEзнач.: boolean (TRUE | FALSE)

Поле секции issuingDistributionPoint: указывает, что данный CRL является indirect CRL (издаётся не тем, кто выпустил сертификаты).⚠️ Булево значение. Связано с использованием CRLIssuer в DistributionPoint.

authorityKeyIdentifier

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-странице не зафиксировано.

keyid

authorityKeyIdentifier = keyid | keyid:alwaysзнач.: keyid | keyid:always | keyid:nonss

Ключевое слово authorityKeyIdentifier: просит включить в AKID subject key identifier издателя. С :always включение обязательно (ошибка при невозможности); с :nonss - только если сертификат не самоподписанный.⚠️ Наиболее распространённый вариант: authorityKeyIdentifier = keyid:always.

issuer

authorityKeyIdentifier = issuer | issuer:alwaysзнач.: issuer | issuer:always | issuer:nonss

Ключевое слово authorityKeyIdentifier: просит включить в AKID серийный номер и distinguished name издателя (grandparent name сертификата субъекта).⚠️ Часто используют связку authorityKeyIdentifier = keyid:always,issuer:always.

always

keyid:always | issuer:always

Квалификатор для keyid/issuer в authorityKeyIdentifier: делает соответствующий элемент AKID обязательным.⚠️ При невозможности включить элемент генерация завершается ошибкой.

nonss

keyid:nonss | issuer:nonss

Квалификатор для keyid/issuer в authorityKeyIdentifier: делает соответствующий элемент AKID условным - он включается только если сертификат не самоподписанный (non self-signed).⚠️ Введён в OpenSSL 4.0 (ветка master); в стабильных 3.x недоступен — используйте always или опускайте квалификатор.

none

authorityKeyIdentifier = none | subjectKeyIdentifier = none

Значение для authorityKeyIdentifier и subjectKeyIdentifier: расширение не включается (AKID/SKID не добавляется).⚠️ Для AKID none означает, что AKID не включается; не комбинируется с keyid/issuer. Для SKID - расширение SKID не включается.

subjectKeyIdentifier

subjectKeyIdentifier = hash | none | знач.: none | hash | hex-строка (байты можно разделять ‘:’)

Расширение Subject Key Identifier (SKID): идентифицирует открытый ключ субъекта. Принимает три формы: none - расширение не включается; hash - keyIdentifier вычисляется как 160-битный SHA-1 хеш; шестнадцатеричная строка - используется указанное значение напрямую.⚠️ Hex-форма допускает разделители ‘:’ между байтами. Man-страница не объявляет hash значением по умолчанию - это три равноправные формы (default не документирован).

hash

subjectKeyIdentifier = hash

Значение subjectKeyIdentifier: вычислить keyIdentifier как 160-битный SHA-1 хеш.⚠️ Одна из трёх форм SKID; man-страница не называет её значением по умолчанию.

critical

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

-gencrl

Генерирует CRL на основе информации в файле базы (index.txt). Основная опция для выпуска списка отзыва. Требует хотя бы один из параметров срока (-crldays/-crlhours/-crlsec или default_crl_days/default_crl_hours в конфиге).⚠️ Критично: имя ровно -gencrl, частая ошибка — писать -gen_crl или -crlgen. Если crlnumber-файл присутствует, в CRL вставляется номер (расширение crlNumber).

-revoke

-revoke filenameзнач.: filename — путь к файлу сертификата (PEM)

Отзывает сертификат из указанного файла: меняет его запись в index.txt со статуса V (valid) на R (revoked) и проставляет дату отзыва. Сопровождается -crl_reason / -crl_hold / -crl_compromise / -crl_CA_compromise.⚠️ Отзыв сам по себе не публикует CRL — после -revoke нужно отдельно выполнить -gencrl. Сертификат должен присутствовать в базе.

-valid

-valid filenameзнач.: filename — путь к файлу сертификата

Добавляет в базу запись о сертификате как о действительном (статус V) без его подписания. Для ручного приведения базы в соответствие.⚠️ Применять с осторожностью: запись вносится напрямую в index.txt без проверки политики.

-crl_reason

-crl_reason reasonзнач.: unspecified, keyCompromise, CACompromise, affiliationChanged, superseded, cessationOfOperation, certificateHold, removeFromCRL

Указывает причину отзыва (CRL reason code). Сопоставление значений регистронезависимо. Установка любой причины делает CRL версии 2 (v2).⚠️ removeFromCRL и certificateHold имеют особую семантику. Имя ровно -crl_reason.

-crl_hold

-crl_hold instructionзнач.: instruction — OID hold-инструкции (holdInstructionNone, holdInstructionCallIssuer, holdInstructionReject)

Устанавливает код причины certificateHold и hold instruction в указанный OID. Для временной приостановки (hold) сертификата.⚠️ Аргумент — именно OID. Делает CRL v2.

-crl_compromise

-crl_compromise timeзнач.: time в формате YYYYMMDDHHMMSSZ

Устанавливает причину keyCompromise и время компрометации в указанное значение.⚠️ Время в формате GeneralizedTime (4-значный год) с суффиксом Z (UTC). Делает CRL v2.

-crl_CA_compromise

-crl_CA_compromise timeзнач.: time в формате YYYYMMDDHHMMSSZ

То же, что -crl_compromise, но причина устанавливается в CACompromise (компрометация ключа самого CA).⚠️ Обратите внимание на регистр в имени: -crl_CA_compromise (CA заглавными).

-crl_lastupdate

-crl_lastupdate timeзнач.: YYMMDDHHMMSSZ или YYYYMMDDHHMMSSZ

текущее время

Явно задаёт поле lastUpdate (thisUpdate) генерируемого CRL. Без опции — текущее время.⚠️ Принимает и UTCTime (2-значный год), и GeneralizedTime (4-значный год).

-crl_nextupdate

-crl_nextupdate timeзнач.: YYMMDDHHMMSSZ или YYYYMMDDHHMMSSZ

Явно задаёт поле nextUpdate генерируемого CRL. Если присутствует, значения -crldays/-crlhours/-crlsec игнорируются.⚠️ Взаимоисключает относительные сроки.

-crldays

-crldays numзнач.: num — число дней

Число дней до следующего выпуска CRL — помещается в поле nextUpdate.⚠️ Конфиг-аналог: default_crl_days. Игнорируется при -crl_nextupdate. Складывается с -crlhours/-crlsec.

-crlhours

-crlhours numзнач.: num — число часов

Число часов до следующего выпуска CRL (добавляется к -crldays при вычислении nextUpdate).⚠️ Конфиг-аналог: default_crl_hours. Игнорируется при -crl_nextupdate.

-crlsec

-crlsec numзнач.: num — число секунд

Число секунд до следующего выпуска CRL (добавляется к -crldays/-crlhours).⚠️ Конфиг-аналога нет (только командная строка). Игнорируется при -crl_nextupdate.

-crlexts

-crlexts sectionзнач.: section — имя секции конфига

Секция конфига с расширениями CRL. Если секции нет — CRL v1; если есть (даже пустая) — CRL v2.⚠️ опция командной строки -crlexts (без подчёркивания после crl), а одноимённый ключ конфига — crl_extensions. Не путать.

-status

-status serialзнач.: serial — серийный номер (hex)

Отображает статус отзыва сертификата с указанным серийным номером и завершает работу.⚠️ Удобно для проверки записи без открытия index.txt вручную.

-updatedb

-updatedb

Обновляет индекс: помечает истёкшие сертификаты, переводя статус с V на E (expired).⚠️ Без -updatedb истёкшие сертификаты остаются со статусом V (флаг E не проставляется автоматически).

-md

-md algзнач.: alg — имя дайджеста (sha256, sha512, default, …)

Алгоритм хеширования для подписи. Применяется и к подписи CRL.⚠️ Конфиг-аналог: default_md. Для Ed25519/Ed448 дайджест не задаётся. Значение ‘default’ выбирает дайджест по типу ключа.

-rand_serial

-rand_serial

Генерировать большой случайный серийный номер сертификата. Переопределяет использование файла serial.⚠️ На нумерацию CRL (crlnumber) не влияет. Для соответствия требованиям к энтропии серийников.

-notext

-notext

Не выводить текстовую форму сертификата в выходной файл — только PEM.⚠️ На содержимое CRL не влияет.

-config

-config filenameзнач.: filename — путь к конфигу

Указывает используемый конфигурационный файл (openssl.cnf).⚠️ Без -config используется конфиг из окружения (OPENSSL_CONF / встроенный путь).

-name / -section

-name section (синоним -section section)знач.: section — имя секции конфига

Задаёт секцию конфига для использования (переопределяет default_ca в секции [ca]). -section — полный синоним.⚠️ Обычно секция называется CA_default и содержит все CRL/база-ключи (database, crlnumber, default_crl_days и т.д.).

-keyfile / private_key

-keyfile filename|uriзнач.: приватный ключ CA

Приватный ключ CA, которым подписываются запросы и CRL. Должен соответствовать -cert.⚠️ Конфиг-аналог: private_key. При -gencrl нужен для подписи списка отзыва.

-cert / certificate

-cert fileзнач.: сертификат CA

Сертификат CA (должен соответствовать ключу). Выступает издателем (issuer) для CRL.⚠️ Конфиг-аналог: certificate.

-outdir / new_certs_dir

-outdir directoryзнач.: каталог вывода новых сертификатов

Каталог для новых сертификатов. Командный аналог конфиг-ключа new_certs_dir.⚠️ К CRL прямого отношения не имеет, но часть базовой инфраструктуры CA.

crlnumber

crlnumber = $dir/crlnumberзнач.: путь к файлу со следующим номером CRL (hex)

Файл со следующим номером CRL (hex). Номер вставляется в CRL (crlNumber) только если файл существует.⚠️ Опциональный ключ. Без файла CRL выпускается без crlNumber. Обычно инициализируется 1000 или 01.

crl

crl = $dir/crl.pemзнач.: путь к файлу CRL

Исторический ключ, указывает расположение текущего CRL. Присутствует в типовых конфигах [CA_default].⚠️ В современных версиях менее значим, чем crlnumber/default_crl_days; результат обычно задаётся опцией вывода.

crl_extensions

crl_extensions = crl_extзнач.: имя секции с расширениями CRL

Fallback-аналог опции -crlexts: секция с расширениями CRL. Есть секция (даже пустая) → CRL v2; нет → CRL v1.⚠️ ключ конфига crl_extensions (с подчёркиванием), опция командной строки -crlexts (без). Типовая секция содержит authorityKeyIdentifier.

default_crl_days

default_crl_days = 30знач.: число дней

Конфиг-аналог -crldays. Используется, если в командной строке нет -crldays. Для генерации CRL нужен хотя бы один из default_crl_days/default_crl_hours.⚠️ Если ни дней, ни часов не задано — генерация CRL завершится ошибкой.

default_crl_hours

default_crl_hours = 24знач.: число часов

Конфиг-аналог -crlhours. Используется, если нет -crlhours. Хотя бы один из default_crl_days/default_crl_hours обязателен для генерации CRL.⚠️ Можно комбинировать с default_crl_days.

default_md

default_md = sha256знач.: имя дайджеста (sha256, sha512, default, …)

Конфиг-аналог -md. Алгоритм для подписи сертификатов и CRL. Обязательный (кроме Ed25519/Ed448).⚠️ Значение ‘default’ выбирает дайджест по типу ключа. md5/sha1 для подписи не рекомендуются.

database

database = $dir/index.txtзнач.: путь к текстовому файлу базы

Текстовый файл базы (index.txt). Обязательный. Может быть изначально пустым. Учёт выпущенных/отозванных, на его основе строится CRL.⚠️ Критичен: RESTRICTIONS предупреждают о трудности восстановления при повреждении. Рядом ведётся index.txt.old.

unique_subject

unique_subject = yesзнач.: yes | no

yes

yes — действительные записи (V) должны иметь уникальные subject; no — допускаются дубли subject. Для смены (roll-over) сертификатов CA рекомендуется no.⚠️ Фактическое значение фиксируется в файле index.txt.attr при добавлении первой записи. По умолчанию yes.

copy_extensions

copy_extensions = noneзнач.: none | copy | copyall

none

Обработка расширений из CSR при выпуске: none — игнорировать; copy — копировать отсутствующие; copyall — копировать все.⚠️ Безопасность: copy/copyall позволяют запросу влиять на содержимое сертификата (SAN, basicConstraints) — осторожно.

serial

serial = $dir/serialзнач.: путь к файлу серийного номера (hex)

Файл со следующим серийным номером сертификатов (hex). Обязательный. Не путать с crlnumber.⚠️ Переопределяется -rand_serial. Для CRL — отдельный файл crlnumber.

policy

policy = policy_matchзнач.: имя секции политики

Конфиг-аналог -policy: секция политики CA (какие поля subject обязательны/совпадают). Обязательный ключ.⚠️ К CRL прямого отношения не имеет, но обязателен для работы CA-команды.

default_ca

default_ca = CA_defaultзнач.: имя секции (обычно CA_default)

В секции [ca] указывает секцию по умолчанию с настройками CA. Переопределяется -name / -section.⚠️ Именно эта секция содержит все CRL/база-релевантные ключи.

index.txt (формат строки)

\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.

index.txt: V

Vзнач.: V

Статус Valid (действительный): сертификат выпущен и не отозван.⚠️ Истёкшие остаются V до openssl ca -updatedb.

index.txt: R

Rзнач.: R

Статус Revoked (отозванный). Поле 3 содержит дату отзыва (и опционально причину).⚠️ Устанавливается -revoke. В CRL попадают именно записи R.

index.txt: E

Eзнач.: E

Статус Expired (истёкший): срок действия истёк.⚠️ Проставляется только после openssl ca -updatedb; иначе истёкший остаётся V.

index.txt: ?

?знач.: ?

Служебный/недокументированный флаг неизвестного статуса. В нормальной эксплуатации не используется; обычно указывает на повреждённую запись.⚠️ В официальной документации как штатный статус не описан. При появлении ‘?’ проверьте целостность базы.

index.txt.attr

unique_subject = yes|noзнач.: файл из одной строки

unique_subject = yes

Файл атрибутов базы рядом с index.txt. Одна строка, отражающая unique_subject на момент добавления первой записи.⚠️ Значение фиксируется при первой записи и далее берётся из файла, а не из конфига. Для roll-over рекомендуется no.

crlnumber (файл)

знач.: одна строка — следующий номер 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

-help

Выводит краткую справку по использованию и список опций.⚠️ Печатает usage и завершает работу.

-inform

-inform DER|PEMзнач.: DER, PEM

Формат входного CRL. По документации формат по умолчанию не задан (unspecified).⚠️ Частая ошибка — читать DER без -inform DER. Лучше указывать явно.

-outform

-outform DER|PEMзнач.: DER, PEM

PEM

Формат выходного CRL. По умолчанию PEM.⚠️ Для публикации CRL в точках раздачи (CDP) обычно используют -outform DER.

-in

-in filenameзнач.: filename

stdin

Входной файл. Без опции — стандартный ввод.⚠️ Совместно с -inform определяет интерпретацию входа.

-out

-out filenameзнач.: filename

stdout

Выходной файл. По умолчанию стандартный вывод.⚠️ При информационных опциях без -noout вместе с ними печатается и закодированный CRL.

-dateopt

-dateopt rfc_822|iso_8601знач.: rfc_822, iso_8601

rfc_822

Формат вывода полей дат (lastUpdate/nextUpdate). По умолчанию rfc_822.⚠️ iso_8601 даёт машиночитаемый формат YYYY-MM-DD HH:MM:SSZ.

-key

-key filenameзнач.: filename

Закрытый ключ для (пере)подписи CRL.⚠️ Формат ключа задаётся -keyform.

-keyform

-keyform DER|PEM|P12знач.: DER, PEM, P12

Формат файла закрытого ключа. По умолчанию не задан.⚠️ Применяется вместе с -key.

-text

-text

Печатает CRL в текстовом (человекочитаемом) виде.⚠️ Показывает версию, издателя, даты, номер CRL, список отозванных с серийниками и расширениями. Без -noout печатается и закодированный CRL.

-hash

-hash

Выводит хеш имени издателя CRL — для поиска CRL в каталоге по издателю.⚠️ Печатает хеш имени издателя, не содержимого. Для каталога с именами .r0.

-hash_old

-hash_old

Хеш имени издателя по старому алгоритму (до OpenSSL 1.0.0).⚠️ Только для совместимости с каталогами под OpenSSL < 1.0.0.

-fingerprint

-fingerprint

Выводит отпечаток (fingerprint) CRL.⚠️ Хеш самого CRL — для быстрой идентификации/сверки версии.

-crlnumber

-crlnumber

Выводит номер CRL (поле crlNumber).⚠️ crlNumber монотонно растёт; полезен для контроля актуальности и дельта-CRL.

-issuer

-issuer

Выводит имя издателя (issuer) CRL.⚠️ Формат отображения настраивается через -nameopt.

-lastupdate

-lastupdate

Выводит поле lastUpdate (дата выпуска данного CRL).⚠️ Формат даты — через -dateopt.

-nextupdate

-nextupdate

Выводит поле nextUpdate (дата ожидаемого следующего CRL).⚠️ По нему определяют, не устарел ли CRL. Формат — через -dateopt.

-noout

-noout

Не выводить закодированную версию CRL.⚠️ Используется с -text или отдельными инфо-опциями, чтобы получить только нужное без PEM/DER-дампа.

-nameopt

-nameopt optionзнач.: oneline, multiline, RFC2253, esc_ctrl, utf8 и др. (openssl-namedisplay-options)

Как отображаются имена субъекта/издателя.⚠️ Влияет на -issuer и текстовое представление издателя в -text. Значения через запятую.

-gendelta

-gendelta filenameзнач.: filename

Формирует дельту между основным CRL (из -in) и CRL из этого параметра.⚠️ Пропускается, если -verify не прошла проверку подписи.

-badsig

-badsig

Портит подпись перед записью CRL; для тестирования.⚠️ Для негативного тестирования логики проверки подписи у потребителя.

-verify

-verify

Проверяет подпись CRL. При неудаче программа завершается (дальнейшая обработка, напр. -gendelta, пропускается). Неявно включается при -CApath/-CAfile/-CAstore.⚠️ Нужны сертификаты издателя CA через -CAfile/-CApath/-CAstore.

-CAfile

-CAfile fileзнач.: file

Файл с доверенными сертификатами CA (для проверки подписи CRL).⚠️ Указание неявно включает -verify.

-CApath

-CApath dirзнач.: dir

Каталог с доверенными сертификатами CA (хешированный каталог).⚠️ Указание неявно включает -verify. Готовится через openssl rehash/c_rehash.

-CAstore

-CAstore uriзнач.: uri

URI хранилища доверенных сертификатов CA.⚠️ Указание неявно включает -verify.

-no-CAfile

-no-CAfile

Отключает встроенный по умолчанию файл доверенных CA.⚠️ Запрещает загрузку системного CAfile.

-no-CApath

-no-CApath

Отключает встроенный по умолчанию каталог доверенных CA.⚠️ Запрещает загрузку системного CApath.

-no-CAstore

-no-CAstore

Отключает встроенное по умолчанию хранилище доверенных CA.⚠️ Запрещает загрузку системного CAstore.

-provider

-provider nameзнач.: name

Провайдер OpenSSL для загрузки.⚠️ Можно указывать несколько раз (default, legacy, fips).

-provider-path

-provider-path pathзнач.: path

Путь поиска провайдеров OpenSSL.⚠️ Может задаваться переменной OPENSSL_MODULES.

-propquery

-propquery propqзнач.: propq

Строка запроса свойств для выбора реализаций алгоритмов у провайдеров.⚠️ Ограничивает выбор алгоритмов конкретным провайдером.

-provparam

-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_check

выключено (отзыв не проверяется)

Включает проверку отзыва только для конечного (листового) сертификата через поиск действующего CRL. Если действующий CRL не найден — ошибка.⚠️ Проверяется только лист. Источник CRL: -CRLfile, -crl_download (по CDP) или CRL в trust store. Без источника — ошибка 3 (unable to get certificate CRL).

-crl_check_all

-crl_check_all

выключено

Включает проверку отзыва для всех сертификатов цепочки через поиск действующих CRL для каждого.⚠️ Отличие от -crl_check: весь путь, включая промежуточные CA (для каждого нужен свой CRL). Для полноценной проверки PKI нужен именно -crl_check_all.

-crl_download

-crl_download

выключено

Пытается скачать CRL для сертификатов по их записям CDP (CRL Distribution Points).⚠️ Требует расширения CDP с доступным URL. Имеет смысл только вместе с -crl_check/-crl_check_all. Без CDP/доступа — ошибка 3.

-CRLfile

-CRLfile filename|uriзнач.: файл/URI с одним или несколькими CRL в PEM или DER

Файл или URI с одним или несколькими CRL (PEM/DER) для проверки отзыва.⚠️ Можно указывать несколько раз. Сам по себе не включает проверку — нужен -crl_check/-crl_check_all.

-extended_crl

-extended_crl

выключено

Включает расширенные возможности CRL: косвенные (indirect) CRL и альтернативные ключи подписи CRL.⚠️ Нужно, когда CRL подписан делегированным ключом или используется indirect CRL. Без неё — возможна ошибка 33.

-use_deltas

-use_deltas

выключено

Включает поддержку дельта-CRL (delta CRLs).⚠️ Дельта-CRL содержит только изменения относительно базового. Без опции дельты игнорируются.

-ignore_critical

-ignore_critical

выключено (неизвестные критические расширения → отклонение)

Обычно неподдерживаемое критическое расширение приводит к отклонению (RFC 5280). С этой опцией такие расширения игнорируются.⚠️ Снижает строгость. Подавляет ошибки 34 и 36.

-x509_strict

-x509_strict

выключено

Отключает обходные приёмы для некорректных сертификатов; генерирует ошибки на несоответствии RFC 5280.⚠️ Повышает строгость — может выявить нарушения в полях, относящихся к CRL и keyUsage.

-no_check_time

-no_check_time

проверка времени включена

Подавляет проверку срока действия сертификатов И CRL относительно текущего времени. При заданном -attime подавление не применяется.⚠️ Подавляет ошибки 11 (CRL is not yet valid) и 12 (CRL has expired).

-attime

-attime timestampзнач.: timestamp — секунды с 1 января 1970 (Unix Epoch)

текущее системное время

Проверки достоверности на заданный момент времени, а не на текущее.⚠️ Влияет и на проверку актуальности CRL (lastUpdate/nextUpdate сверяются с этим временем).

-check_ss_sig

-check_ss_sig

выключено

Проверяет подпись последнего сертификата цепочки, если он явно самоподписанный.⚠️ Относится к построению цепочки (корень), не напрямую к CRL.

-partial_chain

-partial_chain

выключено

Разрешает успех при неполной (частичной) цепочке.⚠️ Позволяет доверять промежуточному из trust store без построения до корня. При частичной цепочке отзыв проверяется только до точки доверия.

-trusted_first

-trusted_first

включено всегда (нельзя отключить)

Доверенные сертификаты используются раньше любых из -untrusted. Включена по умолчанию и не может быть отключена.⚠️ Сохранена для совместимости. Влияет на выбор издателя, а значит и на то, чей CRL ищется.

-no_alt_chains

-no_alt_chains

Историческая опция поиска альтернативных цепочек. В современных версиях фактически без эффекта.⚠️ Присутствует в SYNOPSIS для совместимости.

-CAfile

-CAfile fileзнач.: файл с доверенными сертификатами (DER — один; PEM — несколько)

системный файл, если не -no-CAfile

Загружает файл с доверенным сертификатом (DER) или несколькими (PEM).⚠️ Формирует корень доверия, относительно которого строится цепочка и ищутся CRL издателей.

-CApath

-CApath dirзнач.: каталог-хранилище (hashed dir)

системный каталог, если не -no-CApath

Каталог доверенных сертификатов как trust store.⚠️ Файлы индексируются по хэшу (openssl rehash). Там же могут лежать CRL.

-CAstore

-CAstore uriзнач.: URI хранилища доверенных сертификатов

системное хранилище, если не -no-CAstore

Использует uri как хранилище доверенных сертификатов.⚠️ Обобщённый механизм OSSL_STORE.

-no-CAfile / -no-CApath / -no-CAstore

-no-CAfile | -no-CApath | -no-CAstore

Не загружать стандартный файл/каталог/хранилище доверенных сертификатов.⚠️ Полезно, чтобы изолировать проверку только заданными вручную корнями.

-untrusted

-untrusted filename|uriзнач.: файл/URI с сертификатами (PEM)

Недоверенные сертификаты для построения цепочки (например, промежуточные CA).⚠️ Можно несколько раз. Доверенными сами не становятся, но достраивают путь; при -crl_check_all для них тоже ищется CRL.

-trusted

-trusted filename|uriзнач.: файл/URI с сертификатами (PEM)

(Более-менее) доверенные сертификаты; считается доверенным при подходящем положительном атрибуте доверия.⚠️ Можно несколько раз. Альтернатива -CAfile/-CApath для задания корней.

-show_chain

-show_chain

выключено

Показывает построенную цепочку (при успехе). Недоверенные помечаются.⚠️ Помогает понять, какой путь выбран и чьи CRL должны проверяться.

-verbose

-verbose

выключено

Печатает дополнительную информацию о выполняемых операциях.⚠️ Полезно для диагностики проблем с поиском/загрузкой CRL.

-policy_check / -policy / -explicit_policy / -inhibit_any / -inhibit_map / -policy_print

-policy_check ; -policy OID ; -explicit_policy ; -inhibit_any ; -inhibit_map ; -policy_printзнач.: OID для -policy

Подсистема обработки политик сертификации (RFC 5280): включение, добавление OID в user-initial-policy-set и установка соответствующих переменных политики.⚠️ К отзыву прямого отношения не имеют, относятся к строгой валидации цепочки. Приведены для полноты.

-purpose

-purpose purposeзнач.: sslclient, sslserver, nssslserver, smimesign, smimeencrypt, crlsign, ocsphelper, timestampsign, codesign, any

Высокоуровневое назначение целевого сертификата; проверяет пригодность для этой цели.⚠️ При несовпадении — ошибка 26 (unsuitable certificate purpose). Назначение crlsign относится к подписанию CRL.

-issuer_checks

-issuer_checks

Игнорируется (no-op).⚠️ Сохранена для совместимости со старыми скриптами.

-allow_proxy_certs

-allow_proxy_certs

выключено

Разрешает верификацию прокси-сертификатов.⚠️ При запрете — ошибка 40 (proxy certificates not allowed).

-auth_level

-auth_level levelзнач.: level — целое (уровень безопасности)

Устанавливает уровень безопасности аутентификации цепочки.⚠️ Влияет на допустимые алгоритмы/длины ключей при валидации.

-verify_depth

-verify_depth numзнач.: num — макс. число промежуточных CA

Ограничивает цепочку num промежуточными CA-сертификатами.⚠️ При превышении — ошибка 22 (certificate chain too long).

-vfyopt

-vfyopt nm:vзнач.: пара имя:значение (алгоритм-специфично)

Передаёт опции алгоритму подписи при верификации.⚠️ Опция самой команды openssl verify.

0 / X509_V_OK

verify error 0 (X509_V_OK)знач.: ok

Успешная проверка: ошибок нет, отзыв (если проверялся) пройден.⚠️ Текст: «ok». Код 0.

2 / X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT

verify error 2знач.: unable to get issuer certificate

Не найден издатель — цепочку нельзя достроить до доверенного корня.⚠️ Часто отсутствует промежуточный в -untrusted или корень в -CAfile/-CApath.

3 / X509_V_ERR_UNABLE_TO_GET_CRL

verify error 3знач.: unable to get certificate CRL

Не удалось получить CRL, хотя проверка отзыва запрошена. CRL не найден среди источников и не скачан.⚠️ Самая частая ошибка при включении отзыва без корректного источника CRL: проверьте -CRLfile, -crl_download (и CDP), либо CRL в trust store.

5 / X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE

verify error 5знач.: unable to decrypt CRL’s signature

Не удалось проверить подпись CRL ожидаемым открытым ключом.⚠️ Аналог кода 4, но для CRL.

8 / X509_V_ERR_CRL_SIGNATURE_FAILURE

verify error 8знач.: CRL signature failure

Подпись CRL некорректна (CRL подписан не тем ключом или повреждён).⚠️ Найденный CRL не подписан ожидаемым издателем/ключом.

11 / X509_V_ERR_CRL_NOT_YET_VALID

verify error 11знач.: CRL is not yet valid

CRL ещё не вступил в силу: lastUpdate в будущем относительно проверочного времени.⚠️ Часто признак рассинхронизации часов. Подавляется -no_check_time.

12 / X509_V_ERR_CRL_HAS_EXPIRED

verify error 12знач.: CRL has expired

Срок действия CRL истёк: nextUpdate в прошлом — CRL устарел.⚠️ Нужен свежий CRL. Подавляется -no_check_time (но это снижает безопасность).

15 / ERROR_IN_CRL_LAST_UPDATE_FIELD

verify error 15знач.: format error in CRL’s lastUpdate field

Ошибка формата поля lastUpdate CRL — дату не удалось разобрать.⚠️ Текст: «format error in CRL’s lastUpdate field».

16 / ERROR_IN_CRL_NEXT_UPDATE_FIELD

verify error 16знач.: format error in CRL’s nextUpdate field

Ошибка формата поля nextUpdate CRL — дату не удалось разобрать.⚠️ Текст: «format error in CRL’s nextUpdate field».

23 / X509_V_ERR_CERT_REVOKED

verify error 23знач.: certificate revoked

ГЛАВНЫЙ код отзыва: сертификат отозван — присутствует в CRL.⚠️ Появляется только при включённой проверке (-crl_check/-crl_check_all) и доступном CRL с серийником сертификата.

33 / X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER

verify error 33знач.: unable to get CRL issuer certificate

Не найден сертификат издателя CRL — нельзя сопоставить CRL с подписавшим CA.⚠️ Для indirect/делегированных CRL; может потребоваться -extended_crl и нужный сертификат издателя CRL.

34 / UNHANDLED_CRITICAL_EXTENSION

verify error 34знач.: unhandled critical extension

В сертификате критическое расширение, не поддерживаемое OpenSSL; по RFC 5280 отклоняется.⚠️ Подавляется -ignore_critical (с потерей строгости).

35 / X509_V_ERR_KEYUSAGE_NO_CRL_SIGN

verify error 35знач.: key usage does not include CRL signing

Сертификат, которым проверяют подпись CRL, не имеет в keyUsage бита cRLSign.⚠️ Прямо относится к отзыву: издатель CRL не уполномочен подписывать CRL.

36 / UNHANDLED_CRITICAL_CRL_EXTENSION

verify error 36знач.: unhandled critical CRL extension

В самом CRL критическое расширение, не поддерживаемое OpenSSL; CRL отклоняется.⚠️ Подавляется -ignore_critical. Связан с -extended_crl при нестандартных расширениях CRL.

44 / X509_V_ERR_DIFFERENT_CRL_SCOPE

verify error 44знач.: different CRL scope

Область действия (scope) найденного CRL не соответствует проверяемому сертификату.⚠️ При partitioned/indirect CRL и несовпадении IDP. Может потребоваться -extended_crl.

54 / X509_V_ERR_CRL_PATH_VALIDATION_ERROR

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, OID 1.3.6.1.5.5.7.48.1.5) — говорит клиенту «статус самого сертификата responder’а проверять не нужно», разрывая рекурсию «кто проверит проверяющего». Раз статус не проверяется — такой сертификат делают короткоживущим.

Расширенный справочник: расширения сертификата OCSP-responder (секция [ ocsp ])

Показать таблицу — 18 параметров (сертификат OCSP-responder)

Параметр

Синтаксис / значения

По умолч.

Описание и нюансы

basicConstraints

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

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 не нужны и являются ошибкой профиля.

critical

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

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.

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

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

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 отключает расширение. Обычно некритично.

keyid

authorityKeyIdentifier = keyid[:always|:nonss]знач.: keyid, keyid:always, keyid:nonss

Опция authorityKeyIdentifier: просит включить в AKID subject key identifier эмитента. Суффикс always делает включение обязательным (ошибка, если элемент нельзя включить).⚠️ Без суффикса элемент включается, если возможно; always форсирует включение и приводит к ошибке при недоступности SKID эмитента.

issuer

authorityKeyIdentifier = …, issuer[:always|:nonss]знач.: issuer, issuer:always, issuer:nonss

Опция authorityKeyIdentifier: просит включить в AKID серийный номер и issuer distinguished name (имя ‘дедушки’ относительно субъекта) сертификата эмитента.⚠️ Суффикс always форсирует включение; nonss делает элемент условным при условии, что сертификат не самоподписан.

noCheck

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 не проверяется, такой сертификат делают короткоживущим. Обычно некритично.

id-pkix-ocsp-nocheck

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

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

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

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

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

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

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; включено как смежный, документированный на странице параметр.

Arbitrary Extensions (by OID)

<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

-index indexfile

Имя текстового индекс-файла в формате ca, содержащего информацию об отзыве сертификатов. Именно наличие этой опции переключает команду в режим responder.⚠️ Серверная сторона. Критично: без -index команда работает как клиент. При указании -index обязательны также -CA и -rsigner. подробное построчное описание столбцов индекс-файла (статусы V/R/E, даты, серийный номер, DN) в самой man-странице openssl-ocsp не приводится – там сказано лишь “text index file in ca format”; формат описан в документации openssl ca.

-CA

-CA file

CA-сертификаты, соответствующие информации об отзыве в индекс-файле, заданном через -index.⚠️ Серверная сторона. Обязателен в режиме responder (при -index). Принимает PEM, DER или PKCS#12. Не путать с -CAfile/-CApath, которые используются для проверки ответа.

-rsigner

-rsigner file

Сертификат, которым подписываются OCSP-ответы (сертификат OCSP-подписанта).⚠️ Серверная сторона. Обязателен в режиме responder (при -index). Принимает PEM, DER или PKCS#12. Обычно содержит EKU id-kp-OCSPSigning (1.3.6.1.5.5.7.3.9).

-rkey

-rkey file

Приватный ключ для подписи OCSP-ответов; если не указан, используется файл, заданный в -rsigner.⚠️ Серверная сторона. Дословно: “if not present the file specified in the -rsigner option is used”. Удобно, когда сертификат и ключ хранятся раздельно. Документированного строкового значения default нет – поведение по умолчанию описано словесно.

-passin

-passin arg

Источник пароля для приватного ключа подписанта (форматы pass:, env:, file: и др. описаны в openssl-passphrase-options).⚠️ Серверная сторона. Нужна, если ключ из -rkey/-rsigner зашифрован.

-rother

-rother file

Дополнительные сертификаты, включаемые в OCSP-ответ (например, цепочка).⚠️ Серверная сторона. Принимает PEM, DER или PKCS#12. Не действует совместно с -resp_no_certs (когда сертификаты вообще не включаются).

-rsigopt

-rsigopt nm:v

Передать параметры алгоритму подписи при подписании OCSP-ответов в виде пар имя:значение. Имена и значения зависят от алгоритма.⚠️ Серверная сторона. Например, rsa_padding_mode:pss и rsa_pss_saltlen:digest для RSA-PSS.

-rmd

-rmd digest

Хэш-алгоритм (digest), используемый при подписи ответа.⚠️ Серверная сторона. Дословно: “The digest to use when signing the response”. Не путать с -rcid (digest для идентификации сертификата в CertID).

-rcid

-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

-resp_no_certs

Не включать никакие сертификаты в OCSP-ответ.⚠️ Серверная сторона. Флаг без аргумента. Уменьшает размер ответа, если клиент уже доверяет подписанту.

-resp_key_id

-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

-nmin minutes

Число минут, в течение которых актуальна информация об отзыве; используется для поля nextUpdate.⚠️ Серверная сторона. В документации -nmin и -ndays описаны одной формулировкой (“Number of minutes or days…”). Если не заданы ни -nmin, ни -ndays, поле nextUpdate опускается.

-ndays

-ndays days

Число дней, в течение которых актуальна информация об отзыве; используется для поля nextUpdate.⚠️ Серверная сторона. Если не заданы ни -ndays, ни -nmin, поле nextUpdate опускается (свежая информация об отзыве доступна немедленно).

-port

-port portnum

Порт, на котором responder слушает OCSP-запросы. Поддерживаются IPv4 и IPv6. Порт можно задать также через -url. Значение 0 означает автоматический выбор любого доступного порта.⚠️ Серверная сторона. Дословно: “A 0 argument indicates that any available port shall be chosen automatically”. Встроенный сервер предназначен преимущественно для тестирования.

-url

-url responder_url

URL OCSP-responder. В режиме клиента задаёт адрес сервиса; на стороне responder через URL-форму можно задать порт прослушивания (альтернатива -port).⚠️ Клиентская и серверная сторона. Документация для -port явно указывает: “The port may also be specified using the -url option”.

-nrequest

-nrequest number

unlimited

OCSP-сервер завершит работу после получения number запросов.⚠️ Серверная сторона. Дословно: “default unlimited” – без ограничения (работает бесконечно).

-multi

-multi process-count

Запустить указанное число дочерних процессов responder; родительский процесс перезапускает дочерние по мере необходимости. Дочерние процессы отслеживают изменения индекс-файла CA и автоматически перезагружают его.⚠️ Серверная сторона. Дословно: “Child processes will detect changes in the CA index file and automatically reload it”. Доступно только на POSIX-системах. На Windows не работает.

-timeout

-timeout seconds

Тайм-аут соединения с OCSP-responder в секундах. На POSIX-системах при работе в роли responder также ограничивает время ожидания запроса от клиента (с момента приёма соединения до полного получения запроса).⚠️ Клиентская и серверная сторона. Серверное поведение (ограничение ожидания запроса) действует только на POSIX-системах.

-ignore_err

-ignore_err

Игнорировать некорректные (malformed) запросы или ответы. В роли клиента – повторить попытку при получении некорректного ответа. В роли responder – продолжать работу, а не завершаться при получении некорректного запроса.⚠️ Клиентская и серверная сторона. Флаг без аргумента. man-страница master описывает обе семантики: клиентскую («retry if a malformed response is received») и серверную («continue running instead of terminating upon receiving a malformed request»).

-nonce

-nonce

Добавить расширение OCSP nonce к запросу. Если запрос подаётся через -reqin, nonce обычно не добавляется; -nonce принудительно добавляет nonce.⚠️ Клиентская сторона (создание/отправка запроса). Документация не описывает зеркалирование nonce из запроса в ответ на стороне responder.

-no_nonce

-no_nonce

Отключить добавление OCSP nonce. При создании запроса (через -cert/-serial) nonce добавляется автоматически; -no_nonce это переопределяет.⚠️ Клиентская сторона. Парная к -nonce. Серверное “подавление nonce в ответе” в man-странице не описано.

-badsig

-badsig

Испортить подпись ответа перед его записью; полезно для тестирования.⚠️ Серверная сторона. Флаг без аргумента. Только для отладки/тестов – корректный клиент должен отвергать такой ответ.

-reqin

-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

-respout filename

Записать DER-кодированный OCSP-ответ в файл. На стороне responder позволяет сохранить сгенерированный ответ в файл.⚠️ Серверная сторона. Дословно: “Write out the DER-encoded OCSP request or response to filename”.

-text

-text

Вывести текстовую форму OCSP-запроса, ответа или обоих.⚠️ Клиентская и серверная сторона. Дословно: “Print out the text form of the OCSP request, response or both respectively” (общая формулировка для -req_text/-resp_text/-text).

-resp_text

-resp_text

Вывести текстовую форму OCSP-ответа.⚠️ Клиентская и серверная сторона. Описана общей формулировкой вместе с -req_text и -text (“request, response or both respectively”); -resp_text относится к ответу.

-out

-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

-issuer filename

Указывает сертификат текущего издателя (CA). Из него берутся имя и открытый ключ для вычисления CertID запрашиваемых сертификатов.⚠️ Можно указывать несколько раз. КРИТИЧНО: должен предшествовать любым опциям -cert, к которым относится. Вход может быть в формате PEM, DER или PKCS#12.

-cert

-cert filename

Добавляет сертификат из файла filename в запрос. Сертификат издателя берётся из предыдущей опции -issuer.⚠️ Можно использовать многократно для запроса статуса нескольких сертификатов. Издатель берётся из предшествующего -issuer.

-serial

-serial numзнач.: десятичное целое; шестнадцатеричное при префиксе 0x; отрицательное при префиксе ‘-’

То же, что -cert, но в запрос добавляется сертификат с серийным номером num (а не из файла).⚠️ Требует предшествующего -issuer.

-no_certs

-no_certs

Не включать никакие сертификаты в подписанный запрос.⚠️ Применяется к подписанному запросу (см. -signer).

-signer

-signer filename

Подписать OCSP-запрос сертификатом, указанным в этой опции (подписанный запрос).⚠️ Подпись запроса нужна только если ответчик её требует; большинство публичных responder-ов её не требуют. Вход может быть в формате PEM, DER или PKCS#12.

-signkey

-signkey filename

Закрытый ключ для подписи OCSP-запроса.⚠️ Если -signkey не задан, закрытый ключ читается из того же файла, что и сертификат -signer.

-sign_other

-sign_other filename

Дополнительные сертификаты, включаемые в подписанный запрос.⚠️ Вход может быть в формате PEM, DER или PKCS#12.

-nonce

-nonce

nonce добавляется по умолчанию при формировании запроса из -cert/-serial

Добавляет расширение OCSP nonce в запрос (защита от replay-атак: ответ должен содержать тот же nonce).⚠️ При подаче запроса через -reqin nonce по умолчанию не добавляется; -nonce форсирует его добавление.

-no_nonce

-no_nonce

Отключает добавление OCSP nonce в запрос (переопределяет автоматическое добавление).⚠️ Нужен при работе с ответчиками, отдающими предсгенерированные (кэшированные) ответы без nonce.

-url

-url responder_url

Задаёт хост ответчика и, опционально, порт и путь через URL. Поддерживаются HTTP и HTTPS (SSL/TLS).⚠️ Альтернатива комбинации -host/-path.

-host

-host host:port

Если задан -host, OCSP-запрос отправляется на узел host по порту port. Host может быть доменным именем или IP-адресом.⚠️ IPv6-адрес заключается в квадратные скобки. Используется вместе с -path вместо -url.

-path

-path pathname

/

Задаёт HTTP-путь (pathname) для запроса. Эквивалентно -url со схемой http:// и заданными host, port и path.⚠️ По умолчанию ‘/’. Применяется в паре с -host.

-header

-header name=value

Добавляет HTTP-заголовок name со значением value в отправляемый OCSP-запрос.⚠️ Можно повторять. Синтаксис в man — строго ‘name=value’ (через знак равенства). Часто нужен заголовок Host для виртуальных хостов ответчика.

-proxy

-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

-no_proxy addresses

переменные окружения no_proxy/NO_PROXY

Список IP-адресов и/или DNS-имён серверов, для которых HTTP(S) прокси не используется.⚠️ Разделители — запятые и/или пробелы.

-timeout

-timeout seconds

Таймаут соединения с OCSP-ответчиком в секундах.⚠️ Значение по умолчанию в man не документировано.

-reqout

-reqout file

Записывает сформированный OCSP-запрос в DER-кодировке в файл.⚠️ Удобно для офлайн-формирования запроса и отправки иным транспортом.

-respout

-respout file

Записывает полученный OCSP-ответ в DER-кодировке в файл.⚠️ Для сохранения и последующего анализа ответа.

-reqin

-reqin file

Читает готовый OCSP-запрос (DER) из файла вместо его формирования из -cert/-serial.⚠️ Игнорируется, если формирование запроса подразумевается другими опциями. При -reqin nonce по умолчанию не добавляется (см. -nonce).

-respin

-respin file

Читает готовый OCSP-ответ (DER) из файла вместо отправки запроса ответчику.⚠️ Позволяет проверять/печатать ранее сохранённый ответ офлайн. Игнорируется, если создание запроса/ответа подразумевается другими опциями.

-req_text

-req_text

Печатает текстовое (человекочитаемое) представление OCSP-запроса.

-resp_text

-resp_text

Печатает текстовое представление OCSP-ответа. Здесь выводится статус сертификата (good/revoked/unknown).

-text

-text

Печатает текстовое представление запроса, ответа или обоих.⚠️ Эквивалентно -req_text + -resp_text.

-out

-out filename

стандартный вывод (stdout)

Задаёт файл для вывода. По умолчанию вывод направляется в стандартный поток (stdout).⚠️ Перенаправляет текстовый/печатный вывод команды.

-CAfile

-CAfile file

Файл с доверенными CA-сертификатами для построения цепочки доверия при проверке подписи OCSP-ответа.⚠️ Подробности — в openssl-verification-options(1), раздел Trusted Certificate Options.

-CApath

-CApath dir

Каталог с доверенными CA-сертификатами (хешированные имена) для проверки ответа.⚠️ См. openssl-verification-options(1).

-CAstore

-CAstore uri

URI хранилища доверенных CA-сертификатов для проверки ответа.⚠️ См. openssl-verification-options(1).

-no-CAfile

-no-CAfile

Не загружать доверенные CA-сертификаты из стандартного файлового расположения OpenSSL.⚠️ Пишется через дефис (-no-CAfile), а не через подчёркивание.

-no-CApath

-no-CApath

Не загружать доверенные CA-сертификаты из стандартного каталога OpenSSL.⚠️ Пишется через дефис.

-no-CAstore

-no-CAstore

Не загружать доверенные CA-сертификаты из стандартного хранилища OpenSSL.⚠️ Пишется через дефис.

-verify_other

-verify_other file

Файл или URI с дополнительными сертификатами, среди которых ищется сертификат, подписавший OCSP-ответ.⚠️ Нужен, когда сертификат подписанта ответа не вложен в сам ответ. Вход может быть в формате PEM, DER или PKCS#12.

-trust_other

-trust_other

Сертификаты из -verify_other считаются явно доверенными, дополнительные проверки над ними не выполняются.⚠️ Ослабляет проверку: используйте только для заведомо доверенных сертификатов ответчика.

-VAfile

-VAfile file

Файл или URI с явно доверенными сертификатами ответчика (Validation Authority).⚠️ Эквивалентно одновременному заданию -verify_other и -trust_other.

-no_intern

-no_intern

Игнорировать сертификаты, вложенные в OCSP-ответ, при поиске сертификата подписанта.⚠️ Заставляет искать подписанта только среди -verify_other/-VAfile.

-no_signature_verify

-no_signature_verify

Не проверять подпись на OCSP-ответе.⚠️ Допускает недействительные подписи — обычно только для тестирования.

-no_cert_verify

-no_cert_verify

Вообще не проверять сертификат подписанта OCSP-ответа.⚠️ Позволяет подписать ответ любым сертификатом — только для тестирования.

-no_cert_checks

-no_cert_checks

Не выполнять дополнительные проверки сертификата подписанта OCSP-ответа.⚠️ Отключает специфичные для OCSP проверки, оставляя базовую проверку цепочки.

-no_chain

-no_chain

Не использовать сертификаты из ответа как дополнительные недоверенные CA-сертификаты при построении цепочки.

-no_explicit

-no_explicit

Не доверять корневому CA явно, даже если он помечен как доверенный для подписи OCSP.

-noverify

-noverify

Не пытаться проверять ни подпись OCSP-ответа, ни значения nonce; также отключает всю проверку сертификата ответчика.⚠️ Полное отключение проверки ответа; обычно только для отладки.

-validity_period

-validity_period nsec

300 (5 минут)

Допустимый диапазон ошибки в секундах при проверке актуальности OCSP-ответа (компенсация рассинхронизации часов между клиентом и ответчиком).⚠️ Документированный default — 5 минут. Слишком малое значение приведёт к ложным отказам при расхождении часов.

-status_age

-status_age age

по умолчанию эта проверка не выполняется

Если в ответе отсутствует поле nextUpdate, проверяется, что возраст поля thisUpdate (notBefore) не старше age секунд.⚠️ Защита от приёма устаревших ответов без явной границы действия.

-rcid

-rcid digest

тот же алгоритм, что использован в запросе

Задаёт алгоритм хеширования для идентификации сертификата (CertID) в OCSP-ОТВЕТЕ.⚠️ Не путать с digest-опцией запроса. Допустим любой digest, поддерживаемый openssl-dgst(1).

-digest

-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

-sha1

значение по умолчанию для CertID

Использовать SHA-1 как алгоритм хеширования для CertID в запросе (частный случай digest-опции).⚠️ Де-факто стандарт для OCSP CertID; совместим с большинством responder-ов. В man отдельно не перечислен — это пример digest-опции.

-sha256

-sha256

Использовать SHA-256 как алгоритм хеширования для CertID (частный случай digest-опции).⚠️ Поддерживается не всеми ответчиками; при несовпадении CertID ответ будет ‘unknown’. В man отдельно не перечислен — пример digest-опции.

good

Cert Status: goodзнач.: good | revoked | unknown

Значение статуса в OCSP-ответе: сертификат не числится отозванным на момент ответа (положительный статус).⚠️ Не документировано в man openssl-ocsp(1) напрямую — определено RFC 6960 (CertStatus), печатается в текстовом выводе (-resp_text/-text). ‘good’ не гарантирует, что сертификат был выпущен.

revoked

Cert Status: revoked

Значение статуса: сертификат отозван. Сопровождается временем отзыва (Revocation Time) и, при наличии, причиной (Revocation Reason).⚠️ Источник — RFC 6960, не сам man. Может быть временным (certificateHold) или постоянным.

unknown

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 на стороне веб-сервера

До сих пор речь шла о раздаче статуса. Но веб-сервер и сам — потребитель отзыва, причём в двух ролях:

  1. OCSP stapling — сервер заранее получает OCSP-ответ для своего собственного сертификата и «пристёгивает» его к TLS-рукопожатию, избавляя браузер от похода к responder’у. В nginx это семейство ssl_stapling*, в Apache — SSLUseStapling/SSLStapling*.

  2. Проверка отзыва клиентских сертификатов (ради чего вся серия) — когда сервер требует клиентский сертификат, он должен проверять его отзыв по 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

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

ssl_stapling_verify on | off;знач.: on | off

ssl_stapling_verify off;

Включает или выключает проверку (верификацию) OCSP-ответов сервером.⚠️ NGINX, контекст: http, server. Появилась в 1.3.7. Для проверки в ssl_trusted_certificate должны быть указаны сертификат издателя серверного сертификата, корневой и все промежуточные сертификаты. По умолчанию выключена; при включении неполная цепочка доверия приводит к ошибке проверки ответа.

ssl_stapling_file

ssl_stapling_file file;знач.: путь к файлу (DER-формат)

При установке stapled OCSP-ответ берётся из указанного файла вместо запроса к OCSP-респондеру, указанному в серверном сертификате.⚠️ NGINX, контекст: http, server. Появилась в 1.3.7. Файл должен быть в формате DER, как его выдаёт команда openssl ocsp. Полезно при недоступности респондера из сети сервера; требует периодического обновления файла вручную/по cron, иначе ответ протухнет.

ssl_stapling_responder

ssl_stapling_responder url;знач.: URL вида http://…

Переопределяет URL OCSP-респондера, указанный в расширении сертификата Authority Information Access (AIA).⚠️ NGINX, контекст: http, server. Появилась в 1.3.7. Поддерживаются только респондеры по http:// (https:// не поддерживается). Применяется именно к stapling серверного сертификата.

ssl_trusted_certificate

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

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

ssl_crl file;знач.: путь к файлу (PEM)

Задаёт файл со списком отозванных сертификатов (CRL) в формате PEM, используемый для проверки клиентских сертификатов.⚠️ NGINX, контекст: http, server. Появилась в 0.8.7. При использовании промежуточных сертификатов их CRL должны быть указаны в том же файле. Относится к проверке клиентских сертификатов (mutual TLS), не к серверному сертификату.

ssl_verify_client

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

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

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

ssl_ocsp_responder url;знач.: URL вида http://…

Переопределяет URL OCSP-респондера из расширения AIA для валидации (ssl_ocsp) клиентских сертификатов.⚠️ NGINX, контекст: http, server. Появилась в 1.19.0. Поддерживаются только http:// респондеры. Не путать с ssl_stapling_responder (тот для серверного сертификата).

ssl_verify_depth

ssl_verify_depth number;знач.: целое число

ssl_verify_depth 1;

Задаёт глубину проверки в цепочке клиентских сертификатов.⚠️ NGINX, контекст: http, server. По умолчанию 1. При длинных цепочках (несколько промежуточных CA) значение по умолчанию может быть мало и проверку придётся увеличить.

SSLUseStapling

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

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

SSLStaplingResponderTimeout secondsзнач.: целое число секунд

SSLStaplingResponderTimeout 10

Задаёт таймаут запросов к OCSP-респондеру при включённом SSLUseStapling.⚠️ Apache mod_ssl, контекст: server config, virtual host. Доступна с httpd 2.4.8. Status: Extension.

SSLStaplingReturnResponderErrors

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

SSLStaplingFakeTryLater on|offзнач.: on | off

SSLStaplingFakeTryLater on

Если включено и не удалось получить OCSP-ответ от респондера, клиенту синтетически прикрепляется ответ tryLater.⚠️ Apache mod_ssl, контекст: server config, virtual host. Доступна с httpd 2.4.8. Status: Extension.

SSLStaplingStandardCacheTimeout

SSLStaplingStandardCacheTimeout secondsзнач.: целое число секунд

SSLStaplingStandardCacheTimeout 3600

Задаёт время хранения в кэше успешных (valid) OCSP-ответов для stapling до их обновления у респондера.⚠️ Apache mod_ssl, контекст: server config, virtual host. Доступна с httpd 2.4.8. По умолчанию 3600 секунд (1 час). Status: Extension.

SSLStaplingErrorCacheTimeout

SSLStaplingErrorCacheTimeout secondsзнач.: целое число секунд

SSLStaplingErrorCacheTimeout 600

Задаёт время хранения в кэше ошибочных (недействительных) OCSP-ответов для stapling.⚠️ Apache mod_ssl, контекст: server config, virtual host. Доступна с httpd 2.4.8. По умолчанию 600 секунд. Status: Extension.

SSLStaplingForceURL

SSLStaplingForceURL uriзнач.: URI OCSP-респондера

Принудительно использует указанный URI OCSP-респондера для stapling вместо URI из расширения AIA сертификата.⚠️ Apache mod_ssl, контекст: server config, virtual host. Доступна с httpd 2.4.8. Значение по умолчанию не документировано. Status: Extension.

SSLStaplingResponseMaxAge

SSLStaplingResponseMaxAge secondsзнач.: целое число секунд; -1 — без ограничения

SSLStaplingResponseMaxAge -1

Задаёт максимально допустимый возраст (свежесть) OCSP-ответов, прикрепляемых через stapling. Значение по умолчанию -1 не накладывает ограничения по возрасту.⚠️ Apache mod_ssl, контекст: server config, virtual host. Доступна с httpd 2.4.8. Status: Extension. Не путать с одноимённой по смыслу SSLOCSPResponseMaxAge — эта директива относится именно к stapling серверного сертификата.

SSLStaplingResponseTimeSkew

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

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

SSLOCSPDefaultResponder uriзнач.: URI OCSP-респондера

Задаёт OCSP-респондер по умолчанию. Если SSLOCSPOverrideResponder не включён, заданный URI используется, только если в проверяемом сертификате не указан респондер.⚠️ Apache mod_ssl, контекст: server config, virtual host. Status: Extension. Относится к валидации клиентских сертификатов (SSLOCSPEnable).

SSLOCSPOverrideResponder

SSLOCSPOverrideResponder on|offзнач.: on | off

SSLOCSPOverrideResponder off

Принудительно использует заданный SSLOCSPDefaultResponder при OCSP-валидации, переопределяя URI респондера из сертификата.⚠️ Apache mod_ssl, контекст: server config, virtual host. Status: Extension.

SSLOCSPNoverify

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

SSLOCSPResponderCertificateFile fileзнач.: путь к файлу (PEM, доверенные сертификаты респондеров)

Задаёт файл с доверенными PEM-кодированными сертификатами OCSP-респондеров, используемыми для проверки подписи OCSP-ответов.⚠️ Apache mod_ssl, контекст: server config, virtual host. Status: Extension. Доступна с httpd 2.4.26 при использовании OpenSSL 0.9.7 и новее. Применяется, когда сертификат респондера не входит в обычную доверенную цепочку.

SSLOCSPProxyURL

SSLOCSPProxyURL urlзнач.: URL HTTP-прокси

Задаёт URL HTTP-прокси, через который выполняются все запросы к OCSP-респондерам.⚠️ Apache mod_ssl, контекст: server config, virtual host. Status: Extension. Доступна с httpd 2.4.19. Полезно, когда у сервера нет прямого доступа к OCSP-респондерам и требуется выход через корпоративный прокси.

SSLOCSPResponderTimeout

SSLOCSPResponderTimeout secondsзнач.: целое число секунд

SSLOCSPResponderTimeout 10

Задаёт таймаут запросов к OCSP-респондерам при включённом SSLOCSPEnable.⚠️ Apache mod_ssl, контекст: server config, virtual host. Status: Extension.

SSLOCSPResponseMaxAge

SSLOCSPResponseMaxAge secondsзнач.: целое число секунд; -1 — без ограничения

SSLOCSPResponseMaxAge -1

Задаёт максимально допустимый возраст (свежесть) OCSP-ответов. Значение по умолчанию -1 не накладывает ограничения; ответ действителен, пока его поле nextUpdate в будущем.⚠️ Apache mod_ssl, контекст: server config, virtual host. Status: Extension.

SSLOCSPResponseTimeSkew

SSLOCSPResponseTimeSkew secondsзнач.: целое число секунд

SSLOCSPResponseTimeSkew 300

Задаёт максимально допустимый рассинхрон времени (time skew) при проверке полей thisUpdate и nextUpdate OCSP-ответа.⚠️ Apache mod_ssl, контекст: server config, virtual host. Status: Extension. По умолчанию 300 секунд.

SSLOCSPUseRequestNonce

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

SSLCARevocationFile file-pathзнач.: путь к файлу (конкатенированные PEM CRL)

Задаёт единый all-in-one файл с конкатенированными PEM-кодированными списками отзыва (CRL) удостоверяющих центров для аутентификации клиентов.⚠️ Apache mod_ssl, контекст: server config, virtual host. Status: Extension. Можно использовать вместо или вместе с SSLCARevocationPath. Читается при старте сервера, пока он работает от root (до сброса привилегий); не перечитывается во время работы — изменения требуют перезапуска сервера.

SSLCARevocationPath

SSLCARevocationPath directory-pathзнач.: путь к каталогу

Задаёт каталог с PEM-кодированными CRL удостоверяющих центров для аутентификации клиентов.⚠️ Apache mod_ssl, контекст: server config, virtual host. Status: Extension. Файлы должны быть PEM-кодированы и доступны через hash-имена; нужно создавать симлинки вида hash-value.rN. Читаются при старте сервера от root (до сброса привилегий); не перечитываются во время работы — изменения требуют перезапуска.

SSLCARevocationCheck

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

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

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

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, поменяв в путях и URL Person на 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, noCheck

  • config — формат конфигурационного файла OpenSSL

Веб-серверы:

Стандарты (RFC):

  • RFC 5280 — профиль X.509-сертификатов и CRL (в т.ч. CDP, AIA, reason codes, критичность расширений)

  • RFC 6960 — Online Certificate Status Protocol (OCSP); OCSPSigning, id-pkix-ocsp-nocheck

  • RFC 6066 — TLS Extensions, в т.ч. Certificate Status Request (OCSP stapling)

  • RFC 7633 — TLS Feature Extension (Must-Staple, status_request)

Прочее: