Проверка отзыва по CRL

CRL (Certificate Revocation List) представляет собой механизм отзыва сертификатов в инфраструктуре открытых ключей (PKI), основанный на централизованно публикуемых списках. Каждый такой список формируется удостоверяющим центром (CA) и содержит серийные номера сертификатов, которые более не считаются доверенными до истечения срока их действия. В контексте JavaScript-библиотеки Jsrsasign работа с CRL позволяет выполнять локальную проверку статуса сертификатов без обращения к онлайн-сервисам типа OCSP, что особенно важно в автономных или высоконагруженных системах.

CRL представляет собой ASN.1-структуру, подписанную приватным ключом удостоверяющего центра. Основные элементы включают:

  • issuer — субъект, выпустивший CRL
  • thisUpdate — дата формирования списка
  • nextUpdate — дата следующего обновления
  • revokedCertificates — массив отозванных сертификатов
  • signatureAlgorithm — алгоритм подписи
  • signatureValue — криптографическая подпись списка

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

  • userCertificate — серийный номер сертификата
  • revocationDate — дата отзыва
  • reasonCode (опционально) — причина отзыва

В PKI-архитектуре CRL используется как офлайн-альтернатива OCSP, особенно в системах, где требуется минимизация сетевых запросов или обеспечение предсказуемой задержки проверки.

Поддержка CRL в Jsrsasign

Библиотека Jsrsasign предоставляет набор инструментов для работы с X.509, PKCS и CRL-структурами через пространство имён X509, KJUR и X509CRL.

Основной класс для работы с CRL:

  • X509CRL — парсинг и анализ CRL
  • X509 — работа с сертификатами
  • KJUR.asn1.x509 — генерация и обработка ASN.1 структур

Загрузка и разбор CRL

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

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

CRL должен быть криптографически валиден и подписан доверенным CA. Jsrsasign позволяет проверить подпись:

const caCert = new X509();
caCert.readCertPEM(caPem);

const publicKey = caCert.getPublicKey();

const isValid = crl.verifySignature(publicKey);

Если подпись CRL не проходит проверку, список считается недостоверным независимо от его содержимого.

Интеграция проверки CRL в валидацию сертификата

Полноценная проверка сертификата включает несколько этапов:

  1. Проверка цепочки доверия
  2. Проверка срока действия сертификата
  3. Проверка отзыва через 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;
}

Оптимизация проверки CRL

При большом объёме отозванных сертификатов линейный поиск становится неэффективным. Для оптимизации используется индексирование:

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 серийные номера могут возвращаться:

  • в HEX без префикса
  • в верхнем регистре
  • с незначащими нулями

Это требует нормализации:

function normalizeSerial(hex) {
    return hex.toLowerCase().replace(/^0+/, '');
}

Без нормализации возможны ложные отрицательные результаты при проверке.

Ограничения CRL-подхода

CRL имеет архитектурные ограничения:

  • задержка между выпуском CRL и фактическим отзывом
  • рост размера списка при масштабировании PKI
  • необходимость регулярной загрузки обновлений
  • отсутствие мгновенного статуса сертификата

В Jsrsasign эти ограничения не компенсируются автоматически и требуют внешней логики обновления CRL и кеширования.

Комбинированные модели проверки

В практических системах CRL часто используется совместно с OCSP и локальным кешированием:

  • CRL как резервный механизм
  • OCSP для онлайн-проверки
  • локальный индекс CRL для массовых проверок

Jsrsasign обеспечивает базовые криптографические операции, но не реализует сетевую инфраструктуру обновления, поэтому интеграция с внешними источниками данных выполняется на уровне приложения.

Работа с множественными CRL

В сложных 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 имеет собственный список отзыва.

Безопасные практики обработки CRL

При реализации проверки отзыва сертификатов через Jsrsasign критичны следующие аспекты:

  • обязательная проверка подписи CRL
  • контроль срока актуальности nextUpdate
  • нормализация серийных номеров
  • кэширование CRL с учётом обновлений
  • защита от подмены источника CRL

Игнорирование любого из этих пунктов приводит к возможности принятия отозванного сертификата как действительного.