Проверка подписи сертификата

Цифровой сертификат X.509 содержит подпись удостоверяющего центра (CA), подтверждающую подлинность содержащихся в нём данных. Проверка подписи — это криптографическая операция, гарантирующая, что сертификат:

  • не был изменён после выпуска
  • действительно подписан доверенным центром сертификации
  • соответствует цепочке доверия

Библиотека jsrsasign предоставляет полный набор инструментов для разбора сертификатов и проверки их подписей с использованием различных алгоритмов (RSA, ECDSA, DSA).


Структура подписи в X.509

Сертификат включает три ключевых компонента:

  1. tbsCertificate (to be signed) — данные, которые подписываются
  2. signatureAlgorithm — алгоритм подписи
  3. signatureValue — сама подпись

Проверка подписи заключается в следующем:

  • извлечение tbsCertificate
  • вычисление хеша
  • проверка подписи с использованием открытого ключа CA

Загрузка сертификата

Сертификат может быть представлен в формате PEM или DER. В jsrsasign используется класс X509.

const { X509 } = require('jsrsasign');

const certPEM = `-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----`;

const x509 = new X509();
x509.readCertPEM(certPEM);

После загрузки объект x509 содержит доступ ко всем полям сертификата.


Извлечение алгоритма подписи

const sigAlg = x509.getSignatureAlgorithmField();

Примеры значений:

  • SHA256withRSA
  • SHA384withECDSA

Это важно для корректной проверки подписи.


Получение подписи и подписываемых данных

const signatureHex = x509.getSignatureValueHex();
const tbsHex = x509.getTBSCertificateHex();
  • signatureHex — подпись
  • tbsHex — исходные данные

Извлечение открытого ключа удостоверяющего центра

Проверка выполняется с использованием публичного ключа CA. Этот ключ может быть:

  • извлечён из сертификата CA
  • загружен отдельно
const { KEYUTIL } = require('jsrsasign');

const caCertPEM = `-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----`;

const caX509 = new X509();
caX509.readCertPEM(caCertPEM);

const pubKey = caX509.getPublicKey();

Проверка подписи

jsrsasign предоставляет класс KJUR.crypto.Signature.

const { KJUR } = require('jsrsasign');

const sig = new KJUR.crypto.Signature({
  alg: sigAlg
});

sig.init(pubKey);
sig.updateHex(tbsHex);

const isValid = sig.verify(signatureHex);

Интерпретация результата

  • true — подпись корректна
  • false — подпись недействительна

Причины ошибки:

  • сертификат был изменён
  • используется неправильный ключ CA
  • неподдерживаемый алгоритм
  • повреждённые данные

Проверка подписи напрямую через X509 API

jsrsasign предоставляет упрощённый метод:

const isValid = x509.verifySignature(pubKey);

Этот способ:

  • автоматически извлекает данные
  • упрощает код
  • снижает риск ошибок

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

Одиночная проверка подписи недостаточна. В реальных системах используется цепочка:

Root CA → Intermediate CA → End Entity Certificate

Каждый сертификат проверяется:

  1. подписью вышестоящего CA
  2. сроком действия
  3. соответствием имени

Пример ручной проверки цепочки:

function verifyChain(cert, issuerCert) {
  const issuerKey = issuerCert.getPublicKey();
  return cert.verifySignature(issuerKey);
}

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

Самоподписанный сертификат подписывает сам себя:

const pubKey = x509.getPublicKey();
const isSelfSigned = x509.verifySignature(pubKey);

Такой сертификат:

  • не подтверждает доверие
  • используется как корневой (Root CA)

Проверка алгоритмов подписи

Важно учитывать криптостойкость:

Алгоритм Статус
SHA1withRSA устарел
SHA256withRSA рекомендуется
SHA384withECDSA безопасный

Получение алгоритма:

const alg = x509.getSignatureAlgorithmField();

Валидация срока действия

Проверка подписи не гарантирует, что сертификат актуален:

const now = new Date();

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

const isValidTime = now >= new Date(notBefore) && now <= new Date(notAfter);

Проверка отзыва сертификата

jsrsasign не выполняет автоматическую проверку CRL или OCSP, но можно:

  • извлечь URL CRL
  • выполнить HTTP-запрос
  • проверить наличие сертификата в списке
const cdp = x509.getExtCRLDistributionPointsURI();

Типичные ошибки при проверке подписи

1. Неправильный публичный ключ

Используется ключ не того CA.

2. Несоответствие алгоритма

Алгоритм подписи не совпадает с инициализацией Signature.

3. Повреждённый PEM

Ошибки форматирования:

  • отсутствуют заголовки
  • лишние пробелы

4. DER/HEX путаница

Некорректная интерпретация бинарных данных.


Пример полной проверки

const { X509 } = require('jsrsasign');

function verifyCertificate(certPEM, caCertPEM) {
  const cert = new X509();
  cert.readCertPEM(certPEM);

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

  const pubKey = caCert.getPublicKey();

  return cert.verifySignature(pubKey);
}

Оптимизация и безопасность

  • кэширование публичных ключей CA
  • отказ от устаревших алгоритмов
  • проверка всей цепочки, а не одного сертификата
  • использование аппаратных хранилищ ключей (HSM)

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

Библиотека:

  • использует ASN.1 парсинг
  • поддерживает DER/PEM
  • реализует криптографию на чистом JavaScript
  • обеспечивает совместимость с браузером и Node.js

Расширенные сценарии

Проверка нескольких сертификатов:

certList.every((cert, index) => {
  if (index === certList.length - 1) return true;
  return cert.verifySignature(certList[index + 1].getPublicKey());
});

Проверка сертификата клиента (mTLS):

  • извлечение из TLS-сессии
  • проверка цепочки
  • проверка подписи

Практическое значение

Проверка подписи сертификата используется в:

  • HTTPS (TLS)
  • JWT (через X.509)
  • подписи кода
  • PKI-инфраструктурах
  • системах аутентификации

Ключевые выводы

  • подпись — центральный элемент доверия в PKI
  • jsrsasign предоставляет полный контроль над процессом проверки
  • проверка подписи — только часть комплексной валидации
  • безопасность зависит от корректной реализации всей цепочки доверия