Разбор OCSP-ответа

OCSP (Online Certificate Status Protocol) используется для получения актуального статуса X.509-сертификата без необходимости скачивать полный CRL. Ответ OCSP представляет собой ASN.1-структуру, содержащую сведения о проверяемом сертификате, статусе его отзыва и криптографической подписи центра сертификации.

В библиотеке Jsrsasign работа с OCSP-ответами реализована через набор ASN.1-парсеров и утилит для обработки криптографических структур. Основной объект, с которым происходит взаимодействие — OCSPResponse, закодированный в DER.


ASN.1 структура OCSPResponse

OCSPResponse имеет верхнеуровневую структуру:

OCSPResponse ::= SEQUENCE {
   responseStatus         OCSPResponseStatus,
   responseBytes          [0] EXPLICIT ResponseBytes OPTIONAL
}

responseStatus

Поле responseStatus определяет базовый результат обработки запроса:

  • successful (0)
  • malformedRequest (1)
  • internalError (2)
  • tryLater (3)
  • sigRequired (5)
  • unauthorized (6)

Если значение не равно successful, поле responseBytes отсутствует.

responseBytes

Содержит закодированный ответ центра сертификации:

ResponseBytes ::= SEQUENCE {
   responseType   OBJECT IDENTIFIER,
   response       OCTET STRING
}

Для стандартного OCSP используется идентификатор:

id-pkix-ocsp-basic = 1.3.6.1.5.5.7.48.1.1

Внутри response находится структура BasicOCSPResponse.


BasicOCSPResponse

Основной контейнер данных OCSP:

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

Ключевой элемент — ResponseData, содержащий информацию о статусе сертификата.


ResponseData и статус сертификата

ResponseData ::= SEQUENCE {
   version              Version DEFAULT v1,
   responderID          ResponderID,
   producedAt           GeneralizedTime,
   responses            SEQUENCE OF SingleResponse,
   responseExtensions   [1] EXPLICIT Extensions OPTIONAL
}

SingleResponse

Каждый запрос проверяемого сертификата представлен отдельным блоком:

SingleResponse ::= SEQUENCE {
   certID               CertID,
   certStatus           CertStatus,
   thisUpdate           GeneralizedTime,
   nextUpdate           GeneralizedTime OPTIONAL,
   singleExtensions     [0] EXPLICIT Extensions OPTIONAL
}

CertStatus

Статус сертификата представлен одной из трёх форм:

  • good
  • revoked
  • unknown

Разбор OCSP-ответа в Jsrsasign

В Jsrsasign обработка OCSP реализуется через пространство имён KJUR.asn1.ocsp и вспомогательные ASN.1 парсеры.

Основной поток обработки включает:

  1. Декодирование DER-данных
  2. Разбор структуры OCSPResponse
  3. Извлечение BasicOCSPResponse
  4. Анализ ResponseData
  5. Определение CertStatus

Декодирование OCSPResponse

OCSP-ответ обычно поступает в виде DER (binary) или Base64.

const ocspB64 = "MFMwUTBPME0wSzAJBgUrDgMCGgUABBR2...";
const ocspDer = KJUR.b64tohex(ocspB64);

const ocspResp = KJUR.asn1.ocsp.OCSPResponse.getInstanceFromHex(ocspDer);

После декодирования структура становится доступной как ASN.1 объект.


Извлечение responseStatus

const status = ocspResp.getResponseStatus();

if (status !== 0) {
    // ответ не содержит данных о сертификате
}

Доступ к BasicOCSPResponse

const basicResp = ocspResp.getBasicOCSPResponse();
const responseData = basicResp.tbsResponseData;

Поиск статуса сертификата

Внутри responses хранится массив проверок:

const responses = responseData.responses;

Каждый элемент содержит certStatus.


Пример анализа certStatus

responses.forEach(r => {
    const status = r.certStatus.type;

    if (status === "good") {
        console.log("Сертификат действителен");
    }

    if (status === "revoked") {
        console.log("Сертификат отозван");

        const revocationTime = r.certStatus.value.revocationTime;
        const reason = r.certStatus.value.revocationReason;
    }

    if (status === "unknown") {
        console.log("Статус неизвестен");
    }
});

Работа с CertID

Для сопоставления ответа с запросом используется структура CertID:

CertID ::= SEQUENCE {
   hashAlgorithm       AlgorithmIdentifier,
   issuerNameHash      OCTET STRING,
   issuerKeyHash       OCTET STRING,
   serialNumber        CertificateSerialNumber
}

Jsrsasign позволяет сравнивать CertID с локально сформированным запросом OCSP:

const certId = r.certID.serialNumber.toString(16);

Проверка подписи OCSP-ответа

Криптографическая подпись находится в BasicOCSPResponse.signature.

Проверка выполняется через публичный ключ OCSP responder:

const verifier = new KJUR.crypto.Signature({ alg: "SHA256withRSA" });
verifier.init(responderPublicKey);
verifier.updateHex(responseDataHex);

const isValid = verifier.verify(signatureHex);

Extensions в OCSP

Расширения могут содержать:

  • nonce (защита от replay-атак)
  • archiveCutoff
  • serviceLocator

Обработка nonce:

const nonceExt = responseData.responseExtensions;

Сравнение nonce запроса и ответа критично для безопасности проверки.


Особенности обработки revoked статуса

При revoked дополнительно анализируются поля:

  • revocationTime — момент отзыва
  • revocationReason — причина отзыва

Причины могут включать:

  • keyCompromise
  • CACompromise
  • cessationOfOperation
  • superseded
  • unspecified

Работа с временными метками

OCSP использует:

  • thisUpdate — время формирования ответа
  • nextUpdate — срок актуальности

Проверка актуальности:

const now = new Date();
const nextUpdate = new Date(r.nextUpdate);

if (now > nextUpdate) {
    // ответ устарел
}

Типичные ошибки при разборе OCSP

  • отсутствие responseBytes при неуспешном responseStatus
  • неверная кодировка Base64 → DER
  • несоответствие CertID (другой issuer)
  • игнорирование nonce
  • использование неподписанных ответов

Практический сценарий полного разбора

const ocspHex = KJUR.b64tohex(ocspBase64);
const ocsp = KJUR.asn1.ocsp.OCSPResponse.getInstanceFromHex(ocspHex);

if (ocsp.getResponseStatus() !== 0) {
    throw new Error("OCSP response invalid");
}

const basic = ocsp.getBasicOCSPResponse();
const data = basic.tbsResponseData;

data.responses.forEach(r => {
    switch (r.certStatus.type) {
        case "good":
            console.log("OK");
            break;
        case "revoked":
            console.log("REVOKED");
            break;
        case "unknown":
            console.log("UNKNOWN");
            break;
    }
});

Внутренняя модель данных Jsrsasign

Jsrsasign абстрагирует ASN.1 через объектную модель:

  • ASN1Object → базовый контейнер
  • OCSPResponse → верхний уровень
  • BasicOCSPResponse → криптографический блок
  • ResponseData → логическая структура статуса
  • SingleResponse → элемент проверки сертификата

Эта модель позволяет работать с OCSP без ручного парсинга ASN.1-дерева, сохраняя доступ к низкоуровневым полям при необходимости.