Формат OCSP-запроса

OCSP (Online Certificate Status Protocol) используется для проверки статуса X.509-сертификата в режиме реального времени. Запрос OCSP представляет собой ASN.1-структуру, кодируемую в DER-формате и передаваемую обычно через HTTP POST или GET. В библиотеке Jsrsasign формирование OCSP-запроса абстрагируется через высокоуровневые API, однако понимание внутреннего формата необходимо для корректной работы с низкоуровневыми криптографическими механизмами и отладкой.

OCSP-запрос определяется спецификацией RFC 6960 и имеет следующую общую структуру:

  • OCSPRequest

    • tbsRequest (to be signed request)

      • version (по умолчанию v1)

      • requestorName (опционально)

      • requestList

        • Request

          • reqCert

            • hashAlgorithm
            • issuerNameHash
            • issuerKeyHash
            • serialNumber
          • singleRequestExtensions (опционально)

      • requestExtensions (опционально)

    • optionalSignature (опционально)

Ключевым элементом является tbsRequest, который содержит все данные, необходимые для проверки статуса сертификата.

Основной объект запроса: TBSRequest

TBSRequest (To Be Signed Request) — это центральная часть OCSP-запроса. Он включает список проверяемых сертификатов и дополнительные параметры.

В Jsrsasign эта структура формируется через внутренние ASN.1-конструкторы:

  • KJUR.asn1.ocsp.TBSRequest
  • KJUR.asn1.ocsp.Request
  • KJUR.asn1.ocsp.CertID

Каждый запрос OCSP может содержать несколько объектов Request, что позволяет проверять несколько сертификатов одним запросом.

Идентификатор сертификата (CertID)

CertID является ключевым элементом, идентифицирующим сертификат, статус которого проверяется. Он состоит из трех компонентов:

  • hashAlgorithm — алгоритм хеширования (обычно SHA-1 или SHA-256)
  • issuerNameHash — хеш имени издателя сертификата
  • issuerKeyHash — хеш публичного ключа издателя
  • serialNumber — серийный номер сертификата

В Jsrsasign создание CertID осуществляется через вычисление хешей на основе сертификата издателя:

var cert = new X509();
cert.readCertPEM(pemIssuerCert);

var ocsp = new KJUR.asn1.ocsp.OCSPReqBuilder();

var req = ocsp.makeRequest({
  cert: targetCert,
  issuerCert: issuerCert,
  alg: "sha256"
});

При этом библиотека автоматически извлекает необходимые поля из сертификатов и формирует корректный ASN.1-объект.

Формирование requestList

requestList представляет собой последовательность объектов Request. Каждый Request включает CertID и дополнительные расширения.

ASN.1-структура:

  • Request ::= SEQUENCE { reqCert CertID, singleRequestExtensions [0] EXPLICIT Extensions OPTIONAL }

В Jsrsasign добавление нескольких запросов реализуется через массив сертификатов:

var builder = new KJUR.asn1.ocsp.OCSPReqBuilder();

builder.addRequest({
  cert: cert1,
  issuerCert: issuerCert
});

builder.addRequest({
  cert: cert2,
  issuerCert: issuerCert
});

var ocspReq = builder.build();

Расширения OCSP-запроса

OCSP поддерживает расширения, определяемые в формате X.509 Extensions. Они могут включаться как на уровне всего запроса, так и на уровне отдельного Request.

Наиболее распространённые расширения:

  • nonce — защита от replay-атак
  • acceptableResponses — список поддерживаемых форматов ответа
  • serviceLocator — указание OCSP-ресурса

В Jsrsasign nonce добавляется автоматически или задаётся вручную:

var builder = new KJUR.asn1.ocsp.OCSPReqBuilder({
  nonce: "random"
});

Nonce кодируется как OCTET STRING внутри структуры Extensions.

ASN.1 представление OCSPRequest

Полный ASN.1 синтаксис OCSPRequest:

OCSPRequest ::= SEQUENCE { tbsRequest TBSRequest, optionalSignature [0] EXPLICIT Signature OPTIONAL }

TBSRequest ::= SEQUENCE { version [0] EXPLICIT Version DEFAULT v1, requestorName [1] EXPLICIT GeneralName OPTIONAL, requestList SEQUENCE OF Request, requestExtensions [2] EXPLICIT Extensions OPTIONAL }

Signature ::= SEQUENCE { signatureAlgorithm AlgorithmIdentifier, signature BIT STRING, certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }

В большинстве случаев OCSP-запросы не подписываются, и optionalSignature отсутствует.

Кодирование в DER

После формирования структуры Jsrsasign выполняет DER-кодирование. Каждый элемент преобразуется в ASN.1 DER TLV:

  • Tag (тип элемента)
  • Length (длина)
  • Value (значение)

Пример итогового бинарного запроса:

  • SEQUENCE

    • SEQUENCE (tbsRequest)
    • [0] EXPLICIT Signature (опционально)

DER-результат затем преобразуется в Base64 или отправляется в бинарном виде.

Формирование HTTP OCSP-запроса

OCSP-запрос передаётся через HTTP следующим образом:

  • Method: POST (предпочтительно)
  • Content-Type: application/ocsp-request
  • Body: DER-encoded OCSPRequest

Пример формирования запроса:

var req = builder.build();

var httpBody = req.getContentHex();

fetch(ocspUrl, {
  method: "POST",
  headers: {
    "Content-Type": "application/ocsp-request"
  },
  body: new Uint8Array(req.getContentByte())
});

В Jsrsasign методы getContentHex и getContentByte предоставляют доступ к бинарному представлению запроса.

Обработка issuer данных

Для корректного формирования OCSP-запроса необходимо точное соответствие issuerCert. Ошибки в хешировании приводят к отсутствию валидного ответа от OCSP responder.

Алгоритм формирования:

  1. Извлечение Subject из issuerCert
  2. Хеширование Subject Name (issuerNameHash)
  3. Хеширование Subject Public Key (issuerKeyHash)
  4. Подстановка serialNumber целевого сертификата

Jsrsasign выполняет эти операции автоматически через X509-парсер и CryptoJS/forge backend.

Типовые ошибки формирования OCSP-запроса

На уровне структуры OCSPRequest часто возникают следующие проблемы:

  • Несоответствие алгоритма хеширования (SHA-1 vs SHA-256)
  • Неправильный issuerCert
  • Отсутствие nonce при проверке защищённых OCSP responder
  • Некорректное DER-кодирование из-за ручной модификации ASN.1 дерева

Особое внимание требуется при работе с современными CA, которые требуют SHA-256 CertID.

Использование низкоуровневого ASN.1 API Jsrsasign

Jsrsasign предоставляет универсальный ASN.1 API, позволяющий вручную строить OCSP-запрос:

var asn1 = new KJUR.asn1.ASN1Object();

var certId = new KJUR.asn1.ocsp.CertID({
  hashAlg: "sha256",
  issuerCert: issuerCert,
  subjectCert: cert
});

var request = new KJUR.asn1.DERSequence({
  array: [
    certId.asn1obj
  ]
});

Такой подход используется при необходимости полного контроля над структурой запроса.

Внутренняя логика OCSPReqBuilder

OCSPReqBuilder в Jsrsasign выполняет несколько операций:

  • нормализация входных сертификатов
  • построение CertID
  • генерация ASN.1 RequestList
  • добавление Extensions
  • упаковка в TBSRequest
  • формирование финального OCSPRequest

Основное преимущество — автоматическое соблюдение RFC 6960 без ручной работы с ASN.1 уровнями.

Особенности совместимости

OCSPRequest должен быть совместим с серверной реализацией OCSP responder. На практике это означает:

  • строгое соответствие DER encoding
  • корректный порядок полей ASN.1 SEQUENCE
  • поддержка только v1 версии OCSPRequest
  • отсутствие нестандартных расширений без согласования

Некоторые старые responder требуют только SHA-1 CertID, тогда как современные инфраструктуры используют SHA-256.

Итоговая модель формирования запроса

Процесс формирования OCSPRequest в Jsrsasign можно представить как последовательность:

  1. Входные сертификаты (subject + issuer)
  2. Построение CertID (hash + serial)
  3. Формирование Request
  4. Сборка requestList
  5. Добавление extensions (nonce и др.)
  6. Формирование TBSRequest
  7. Опциональная подпись
  8. DER encoding
  9. Передача по HTTP

Эта модель соответствует RFC-спецификации и обеспечивает совместимость с большинством PKI-инфраструктур.