CRL (Certificate Revocation List) представляет собой механизм отзыва сертификатов в инфраструктуре открытых ключей (PKI), основанный на централизованно публикуемых списках. Каждый такой список формируется удостоверяющим центром (CA) и содержит серийные номера сертификатов, которые более не считаются доверенными до истечения срока их действия. В контексте JavaScript-библиотеки Jsrsasign работа с CRL позволяет выполнять локальную проверку статуса сертификатов без обращения к онлайн-сервисам типа OCSP, что особенно важно в автономных или высоконагруженных системах.
CRL представляет собой ASN.1-структуру, подписанную приватным ключом удостоверяющего центра. Основные элементы включают:
Каждый элемент revokedCertificates содержит:
В PKI-архитектуре CRL используется как офлайн-альтернатива OCSP, особенно в системах, где требуется минимизация сетевых запросов или обеспечение предсказуемой задержки проверки.
Библиотека Jsrsasign предоставляет набор инструментов для работы с
X.509, PKCS и CRL-структурами через пространство имён X509,
KJUR и X509CRL.
Основной класс для работы с CRL:
X509CRL — парсинг и анализ CRLX509 — работа с сертификатамиKJUR.asn1.x509 — генерация и обработка ASN.1
структурCRL обычно представлен в формате PEM или DER. В Jsrsasign его можно разобрать следующим образом:
const crlPem = `
-----BEGIN X509 CRL-----
MIIBYDCBygIBATANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJVUzELMAkGA1UE
...
-----END X509 CRL-----
`;
const crl = new X509CRL();
crl.readCertPEM(crlPem);
После загрузки структура становится доступной для анализа:
console.log(crl.getIssuerString());
console.log(crl.getThisUpdate());
console.log(crl.getNextUpdate());
Основной задачей при работе с CRL является проверка наличия серийного номера сертификата в списке отозванных.
const revokedList = crl.getRevokedCertificates();
Каждый элемент массива содержит данные о конкретном отозванном сертификате:
revokedList.forEach(entry => {
console.log(entry.serialNumber);
console.log(entry.revocationDate);
});
Серийные номера обычно представлены в шестнадцатеричном формате.
Процесс проверки сводится к сравнению серийного номера сертификата с содержимым CRL.
function isCertificateRevoked(certPem, crlPem) {
const cert = new X509();
cert.readCertPEM(certPem);
const serial = cert.getSerialNumberHex();
const crl = new X509CRL();
crl.readCertPEM(crlPem);
const revoked = crl.getRevokedCertificates();
for (let i = 0; i < revoked.length; i++) {
if (revoked[i].serialNumber === serial) {
return true;
}
}
return false;
}
Данный подход реализует базовую модель проверки revocation status без учета цепочки доверия.
CRL сам по себе имеет ограниченный срок действия. Игнорирование поля nextUpdate приводит к потенциальным рискам использования устаревших данных.
function isCrlValid(crl) {
const now = new Date();
const thisUpdate = new Date(crl.getThisUpdate());
const nextUpdate = new Date(crl.getNextUpdate());
return now >= thisUpdate && now <= nextUpdate;
}
При интеграции проверки сертификатов необходимо комбинировать проверку валидности CRL и проверку самого сертификата.
CRL должен быть криптографически валиден и подписан доверенным CA. Jsrsasign позволяет проверить подпись:
const caCert = new X509();
caCert.readCertPEM(caPem);
const publicKey = caCert.getPublicKey();
const isValid = crl.verifySignature(publicKey);
Если подпись CRL не проходит проверку, список считается недостоверным независимо от его содержимого.
Полноценная проверка сертификата включает несколько этапов:
Пример объединённой логики:
function validateCertificate(certPem, caPem, crlPem) {
const cert = new X509();
cert.readCertPEM(certPem);
const ca = new X509();
ca.readCertPEM(caPem);
const crl = new X509CRL();
crl.readCertPEM(crlPem);
const now = new Date();
const notBefore = new Date(cert.getNotBefore());
const notAfter = new Date(cert.getNotAfter());
if (now < notBefore || now > notAfter) {
return false;
}
if (!crl.verifySignature(ca.getPublicKey())) {
return false;
}
const serial = cert.getSerialNumberHex();
const revoked = crl.getRevokedCertificates();
for (let i = 0; i < revoked.length; i++) {
if (revoked[i].serialNumber === serial) {
return false;
}
}
return true;
}
При большом объёме отозванных сертификатов линейный поиск становится неэффективным. Для оптимизации используется индексирование:
function buildCrlIndex(crl) {
const index = new Set();
const revoked = crl.getRevokedCertificates();
for (let i = 0; i < revoked.length; i++) {
index.add(revoked[i].serialNumber);
}
return index;
}
Проверка становится константной по времени:
function isRevoked(serial, crlIndex) {
return crlIndex.has(serial);
}
В Jsrsasign серийные номера могут возвращаться:
Это требует нормализации:
function normalizeSerial(hex) {
return hex.toLowerCase().replace(/^0+/, '');
}
Без нормализации возможны ложные отрицательные результаты при проверке.
CRL имеет архитектурные ограничения:
В Jsrsasign эти ограничения не компенсируются автоматически и требуют внешней логики обновления CRL и кеширования.
В практических системах CRL часто используется совместно с OCSP и локальным кешированием:
Jsrsasign обеспечивает базовые криптографические операции, но не реализует сетевую инфраструктуру обновления, поэтому интеграция с внешними источниками данных выполняется на уровне приложения.
В сложных PKI возможно наличие нескольких CRL от разных CA:
function isRevokedMultiCrl(serial, crlList) {
for (let i = 0; i < crlList.length; i++) {
const crl = crlList[i];
const revoked = crl.getRevokedCertificates();
for (let j = 0; j < revoked.length; j++) {
if (revoked[j].serialNumber === serial) {
return true;
}
}
}
return false;
}
Такой подход соответствует иерархическим цепочкам доверия, где каждый уровень PKI имеет собственный список отзыва.
При реализации проверки отзыва сертификатов через Jsrsasign критичны следующие аспекты:
Игнорирование любого из этих пунктов приводит к возможности принятия отозванного сертификата как действительного.