Проверка срока действия

Корректная обработка временных параметров — критически важный аспект при работе с криптографическими структурами. Библиотека Jsrsasign предоставляет инструменты для анализа сроков действия X.509 сертификатов, а также проверки временных атрибутов цифровых подписей.


Основные временные поля X.509

В каждом X.509 сертификате присутствуют два ключевых поля:

  • notBefore — дата и время начала действия сертификата
  • notAfter — дата и время окончания действия сертификата

Эти значения определяют допустимый интервал использования сертификата.

Пример структуры:

Validity:
   Not Before: Jan  1 00:00:00 2024 GMT
   Not After : Dec 31 23:59:59 2026 GMT

Извлечение временных параметров

Для работы с сертификатами используется класс X509:

const x509 = new KJUR.crypto.X509();
x509.readCertPEM(pemCert);

Получение дат:

const notBefore = x509.getNotBefore();
const notAfter = x509.getNotAfter();

Формат возвращаемого значения — строка в ASN.1 формате времени, например:

240101000000Z

Преобразование ASN.1 времени

Для удобной работы требуется преобразование в стандартный JavaScript Date:

function parseASN1Time(asn1Time) {
    const year = parseInt(asn1Time.substr(0, 2), 10) + 2000;
    const month = parseInt(asn1Time.substr(2, 2), 10) - 1;
    const day = parseInt(asn1Time.substr(4, 2), 10);
    const hour = parseInt(asn1Time.substr(6, 2), 10);
    const min = parseInt(asn1Time.substr(8, 2), 10);
    const sec = parseInt(asn1Time.substr(10, 2), 10);

    return new Date(Date.UTC(year, month, day, hour, min, sec));
}

Проверка текущей валидности

Проверка, действителен ли сертификат на текущий момент:

const now = new Date();

const validFrom = parseASN1Time(x509.getNotBefore());
const validTo = parseASN1Time(x509.getNotAfter());

const isValidNow = now >= validFrom && now <= validTo;

Проверка на произвольную дату

В ряде сценариев требуется проверить валидность на конкретный момент времени (например, для архивных подписей):

function isValidAt(date, cert) {
    const x509 = new KJUR.crypto.X509();
    x509.readCertPEM(cert);

    const from = parseASN1Time(x509.getNotBefore());
    const to = parseASN1Time(x509.getNotAfter());

    return date >= from && date <= to;
}

Учет часовых поясов

ASN.1 время хранится в формате UTC. Ошибки возникают при:

  • использовании локального времени вместо UTC
  • неправильной интерпретации формата

Корректная практика:

new Date(Date.UTC(...))

Обработка GeneralizedTime

Если сертификат имеет срок действия после 2049 года, используется формат GeneralizedTime:

YYYYMMDDHHMMSSZ

Пример:

20501231235959Z

Адаптация парсинга:

function parseTime(asn1Time) {
    if (asn1Time.length === 13) {
        return parseASN1Time(asn1Time);
    } else if (asn1Time.length === 15) {
        const year = parseInt(asn1Time.substr(0, 4), 10);
        const month = parseInt(asn1Time.substr(4, 2), 10) - 1;
        const day = parseInt(asn1Time.substr(6, 2), 10);
        const hour = parseInt(asn1Time.substr(8, 2), 10);
        const min = parseInt(asn1Time.substr(10, 2), 10);
        const sec = parseInt(asn1Time.substr(12, 2), 10);

        return new Date(Date.UTC(year, month, day, hour, min, sec));
    }
}

Проверка срока действия цепочки сертификатов

При проверке цепочки необходимо учитывать:

  1. Каждый сертификат должен быть валиден
  2. Интервалы должны пересекаться

Пример логики:

function validateChain(chain) {
    const now = new Date();

    return chain.every(certPEM => {
        const x509 = new KJUR.crypto.X509();
        x509.readCertPEM(certPEM);

        const from = parseTime(x509.getNotBefore());
        const to = parseTime(x509.getNotAfter());

        return now >= from && now <= to;
    });
}

Проверка времени подписи (Timestamp)

Цифровая подпись может содержать атрибут времени — SigningTime.

Извлечение:

const si = new KJUR.asn1.cms.SignerInfo();
si.fromHex(hexSignerInfo);

const signingTime = si.getSigningTime();

После извлечения необходимо проверить:

  • Подпись сделана в период действия сертификата
  • Timestamp доверенный (если используется TSA)

Проверка подписи с учетом времени

function isSignatureTimeValid(signTime, certPEM) {
    const x509 = new KJUR.crypto.X509();
    x509.readCertPEM(certPEM);

    const from = parseTime(x509.getNotBefore());
    const to = parseTime(x509.getNotAfter());

    return signTime >= from && signTime <= to;
}

Проблемы и типичные ошибки

1. Игнорирование временных зон Результат — ложные отрицания валидности.

2. Использование локального времени Следует всегда использовать UTC.

3. Неправильная интерпретация UTCTime Годы до 1950 и после 2049 требуют особой обработки.

4. Проверка только текущего времени Недостаточно для архивных документов.


Проверка истечения срока

Определение, истек ли сертификат:

function isExpired(certPEM) {
    const x509 = new KJUR.crypto.X509();
    x509.readCertPEM(certPEM);

    const now = new Date();
    const to = parseTime(x509.getNotAfter());

    return now > to;
}

Проверка “еще не действителен”

function isNotYetValid(certPEM) {
    const x509 = new KJUR.crypto.X509();
    x509.readCertPEM(certPEM);

    const now = new Date();
    const from = parseTime(x509.getNotBefore());

    return now < from;
}

Комплексная проверка статуса

function getCertStatus(certPEM) {
    const x509 = new KJUR.crypto.X509();
    x509.readCertPEM(certPEM);

    const now = new Date();
    const from = parseTime(x509.getNotBefore());
    const to = parseTime(x509.getNotAfter());

    if (now < from) return "NOT_YET_VALID";
    if (now > to) return "EXPIRED";
    return "VALID";
}

Практические сценарии применения

1. TLS-клиенты Проверка перед установкой соединения

2. Подписи документов Анализ момента подписания

3. Архивные системы Проверка валидности на дату создания

4. PKI-инфраструктура Контроль жизненного цикла сертификатов


Рекомендации

  • Всегда проверять дату начала и окончания
  • Использовать UTC во всех расчетах
  • Поддерживать оба формата времени (UTCTime и GeneralizedTime)
  • Учитывать время подписи, а не только текущее
  • Проверять всю цепочку сертификатов

Внутренние механизмы Jsrsasign

Библиотека не выполняет автоматическую проверку валидности времени — разработчик обязан реализовать эту логику самостоятельно, используя:

  • getNotBefore()
  • getNotAfter()
  • ASN.1 парсеры

Это обеспечивает гибкость, но требует строгого контроля корректности реализации.


Итоговая структура проверки

Полноценная проверка срока действия включает:

  1. Извлечение временных полей
  2. Преобразование в Date
  3. Сравнение с текущим или заданным временем
  4. Проверку цепочки сертификатов
  5. Анализ времени подписи

Такая последовательность обеспечивает корректную и безопасную работу с цифровыми сертификатами и подписями.