Получение дат действия: notBefore и notAfter

В X.509-сертификатах срок действия задаётся обязательным блоком Validity, который включает два ключевых поля:

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

Эти значения определяют временное окно, в течение которого сертификат считается валидным для криптографических операций: подписи, проверки TLS-соединений, аутентификации и других сценариев.

Внутри сертификата эти поля кодируются в формате ASN.1 и могут быть представлены как:

  • UTCTime (для дат до 2050 года)
  • GeneralizedTime (для более поздних дат)

Подход Jsrsasign к работе с сертификатами

Библиотека Jsrsasign предоставляет инструменты для парсинга X.509 и извлечения метаданных без необходимости ручного разбора ASN.1 структуры.

Основной объект для работы с сертификатами:

const x509 = new X509();

Загрузка сертификата в формате PEM:

x509.readCertPEM(pemString);

После загрузки становятся доступны методы для извлечения данных о сроке действия.


Извлечение notBefore и notAfter

Основные методы X509

Jsrsasign предоставляет прямые методы:

x509.getNotBefore()
x509.getNotAfter()

Оба метода возвращают строки в формате:

YYYYMMDDHHMMSSZ

Пример:

20240101000000Z
20251231235959Z

Полный пример получения дат действия сертификата

import { X509 } from "jsrsasign";

const pem = `
-----BEGIN CERTIFICATE-----
MIID...
-----END CERTIFICATE-----
`;

const x = new X509();
x.readCertPEM(pem);

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

console.log("notBefore:", notBefore);
console.log("notAfter:", notAfter);

Преобразование в объект Date JavaScript

Формат YYYYMMDDHHMMSSZ не является напрямую совместимым с Date, поэтому требуется преобразование.

Разбор строки вручную

function parseJsrsasignDate(str) {
  const year = parseInt(str.slice(0, 4), 10);
  const month = parseInt(str.slice(4, 6), 10) - 1;
  const day = parseInt(str.slice(6, 8), 10);
  const hour = parseInt(str.slice(8, 10), 10);
  const minute = parseInt(str.slice(10, 12), 10);
  const second = parseInt(str.slice(12, 14), 10);

  return new Date(Date.UTC(year, month, day, hour, minute, second));
}

Использование преобразования

const startDate = parseJsrsasignDate(x.getNotBefore());
const endDate = parseJsrsasignDate(x.getNotAfter());

console.log(startDate.toISOString());
console.log(endDate.toISOString());

Внутреннее представление дат в Jsrsasign

Jsrsasign работает с ASN.1 значениями времени через внутренний парсер:

  • UTCTime: YYMMDDHHMMSSZ
  • GeneralizedTime: YYYYMMDDHHMMSSZ

При чтении сертификата библиотека автоматически нормализует формат, но возвращает строку без преобразования в Date, оставляя контроль за разработчиком.


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

На основе notBefore и notAfter можно определить актуальность сертификата.

function isCertValid(x509) {
  const now = new Date();

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

  return now >= notBefore && now <= notAfter;
}

Особенности обработки временных зон

Строки notBefore и notAfter всегда завершаются символом Z, что означает UTC-время.

Ключевые особенности:

  • локальная временная зона не используется
  • сравнение должно происходить через UTC
  • Date.UTC обязателен при парсинге

Игнорирование этого приводит к смещению времени и ошибкам в проверке валидности сертификата.


Работа с повреждёнными или неполными сертификатами

При отсутствии блока Validity поведение зависит от структуры PEM:

  • метод readCertPEM может выбросить исключение
  • getNotBefore() / getNotAfter() могут вернуть пустые значения или некорректные строки

Типичная проверка перед использованием:

if (!x.getNotBefore || !x.getNotAfter) {
  throw new Error("Invalid certificate structure");
}

Извлечение даты из цепочки сертификатов

При работе с TLS часто используется цепочка сертификатов. Jsrsasign позволяет разбирать каждый сертификат отдельно:

const certs = pemChain.split("-----END CERTIFICATE-----");

certs.forEach(part => {
  if (part.includes("BEGIN CERTIFICATE")) {
    const x = new X509();
    x.readCertPEM(part + "-----END CERTIFICATE-----");

    console.log(x.getNotBefore(), x.getNotAfter());
  }
});

Форматирование дат для отображения

Для человекочитаемого вывода часто используется локализация:

function formatDate(date) {
  return date.toISOString().replace("T", " ").replace("Z", " UTC");
}

Сравнение дат без преобразования в Date

Jsrsasign возвращает строки фиксированной длины, поэтому возможно лексикографическое сравнение:

const valid = (nowStr >= x.getNotBefore()) && (nowStr <= x.getNotAfter());

Где nowStr формируется в том же формате YYYYMMDDHHMMSSZ.

Такой подход используется для оптимизации в системах с большим количеством проверок сертификатов.


Частые ошибки при работе с notBefore и notAfter

1. Игнорирование UTC

Локальные преобразования приводят к смещению сроков действия.

2. Использование new Date(string)

Строки ASN.1 не поддерживаются напрямую стандартным парсером JavaScript.

3. Сравнение строк разных форматов

Сравниваемые значения должны быть в одном формате (YYYYMMDDHHMMSSZ).

4. Предположение о наличии миллисекунд

X.509 не хранит миллисекунды в Validity.


Внутренняя структура X509 в Jsrsasign

После вызова:

x.readCertPEM(pem);

объект содержит:

  • this.hex — DER в hex-формате
  • this.issuer
  • this.subject
  • this.notBefore
  • this.notAfter

Но доступ к последним обычно осуществляется через методы:

getNotBefore()
getNotAfter()

Использование в системах проверки TLS

Поля notBefore и notAfter применяются в следующих сценариях:

  • проверка серверных SSL-сертификатов
  • клиентская аутентификация
  • валидация цепочек доверия
  • контроль срока действия токенов на основе X.509

Jsrsasign используется как лёгкая альтернатива Node.js crypto для ручного анализа сертификатов без OpenSSL-зависимостей.