OCSP (Online Certificate Status Protocol) применяется для проверки актуального состояния X.509 сертификата в режиме реального времени. В отличие от CRL (Certificate Revocation List), где требуется загружать список отозванных сертификатов целиком, OCSP позволяет получить ответ только по конкретному сертификату, что делает проверку быстрее и эффективнее в прикладных системах.
В JavaScript библиотека Jsrsasign предоставляет инструменты для
работы с криптографией, X.509 сертификатами и OCSP-запросами через
пространство имён KJUR. Практическая реализация проверки
статуса сертификата через OCSP обычно состоит из трёх этапов:
формирование OCSP-запроса, отправка запроса на OCSP-responder и разбор
OCSP-ответа.
Для OCSP-запроса требуются:
В Jsrsasign работа с сертификатом начинается через класс
X509.
const certPEM = `-----BEGIN CERTIFICATE-----
MIID...
-----END CERTIFICATE-----`;
const x509 = new X509();
x509.readCertPEM(certPEM);
const serialNumber = x509.getSerialNumberHex();
const issuer = x509.getIssuerHex();
const subject = x509.getSubjectHex();
Для OCSP важен именно issuer сертификат, так как он используется для построения идентификаторов запроса.
Jsrsasign предоставляет инструменты в пространстве
KJUR.asn1.ocsp для создания OCSPRequest.
Основной объект — OCSPReqBuilder.
const ocsp = KJUR.asn1.ocsp;
const reqBuilder = new ocsp.OCSPReqBuilder();
// добавление идентификатора сертификата
reqBuilder.addCertID({
hashAlg: "sha1",
issuerCert: issuerCertPEM,
subjectCert: certPEM
});
const ocspReq = reqBuilder.build();
const ocspReqB64 = hextob64(ocspReq);
OCSP-запрос формируется в ASN.1 структуру и затем кодируется в DER, после чего может быть отправлен на OCSP responder.
Адрес OCSP responder обычно содержится в расширениях сертификата Authority Information Access (AIA).
Jsrsasign позволяет извлечь его следующим образом:
const aia = x509.getExtAIA();
console.log(aia);
В AIA ищется запись вида:
OCSP - URI:http://ocsp.example.com
Этот URL используется для отправки POST-запроса.
OCSP-запрос передаётся по HTTP POST с типом
application/ocsp-request.
Пример отправки через fetch:
const ocspUrl = "http://ocsp.example.com";
const response = await fetch(ocspUrl, {
method: "POST",
headers: {
"Content-Type": "application/ocsp-request"
},
body: hextorstr(ocspReq)
});
const arrayBuffer = await response.arrayBuffer();
const ocspRespHex = hextob64(sab2hex(arrayBuffer));
Ответ OCSP кодируется в ASN.1 структуру. Jsrsasign предоставляет
парсер OCSPResp.
const ocspResp = new ocsp.OCSPResp(ocspRespHex);
const status = ocspResp.getStatus();
Статус ответа может быть:
0 — SUCCESSFUL1 — MALFORMED REQUEST2 — INTERNAL ERROR3 — TRY LATER5 — SIG REQUIRED6 — UNAUTHORIZEDЕсли статус успешный, можно извлечь информацию о сертификате:
const basicResp = ocspResp.getBasicOCSPResp();
const responses = basicResp.getResponses();
responses.forEach(r => {
const certStatus = r.getCertStatus();
const serial = r.getCertID().getSerialNumber();
});
Статус сертификата определяется через поле
certStatus.
Возможные значения:
Пример обработки:
const certStatus = r.getCertStatus();
if (certStatus === "good") {
console.log("Сертификат действителен");
}
if (certStatus === "revoked") {
console.log("Сертификат отозван");
}
if (certStatus === "unknown") {
console.log("Статус неизвестен");
}
async function checkOCSP(certPEM, issuerCertPEM) {
const x509 = new X509();
x509.readCertPEM(certPEM);
const aia = x509.getExtAIA();
const ocspUrl = aia.match(/http[^\\s]+ocsp[^\\s]+/i)[0];
const reqBuilder = new KJUR.asn1.ocsp.OCSPReqBuilder();
reqBuilder.addCertID({
hashAlg: "sha1",
issuerCert: issuerCertPEM,
subjectCert: certPEM
});
const ocspReq = reqBuilder.build();
const response = await fetch(ocspUrl, {
method: "POST",
headers: {
"Content-Type": "application/ocsp-request"
},
body: hextorstr(ocspReq)
});
const buffer = await response.arrayBuffer();
const ocspRespHex = hextob64(sab2hex(buffer));
const ocspResp = new KJUR.asn1.ocsp.OCSPResp(ocspRespHex);
if (ocspResp.getStatus() !== 0) {
return { error: "OCSP responder error" };
}
const basic = ocspResp.getBasicOCSPResp();
const res = basic.getResponses()[0];
return {
serial: res.getCertID().getSerialNumber(),
status: res.getCertStatus()
};
}
В реальных проектах возникают ограничения:
OCSP responder часто не разрешает кросс-доменные запросы, что делает прямой вызов из браузера невозможным. В таких случаях используется backend-прокси.
OCSP-запросы выполняются для каждого сертификата отдельно, что может создавать нагрузку при массовой проверке.
Ответ OCSP подписан, и Jsrsasign проверяет подпись через цепочку доверия, если переданы корневые сертификаты.
Для дополнительной безопасности можно проверить подпись ответа:
const isValid = basic.verifySignature(ocspSignerCertPEM);
Если подпись не проходит проверку, результат OCSP нельзя считать доверенным.
OCSP-запрос будет отклонён или вернёт UNKNOWN, если issuer не совпадает с цепочкой сертификата.
Некоторые сертификаты не содержат OCSP URL, и запрос невозможно сформировать автоматически.
OCSP требует DER-формат, ошибки часто возникают при неверном преобразовании Base64/HEX.
В прикладных системах проверка OCSP обычно строится следующим образом:
Такой подход снижает нагрузку и обходится без CORS-ограничений.
OCSP ответы имеют поле nextUpdate, определяющее срок их
валидности. Jsrsasign позволяет извлекать временные метки:
const thisUpdate = res.getThisUpdate();
const nextUpdate = res.getNextUpdate();
На основании этих данных можно реализовать локальный кеш, уменьшающий количество запросов к OCSP responder.