Цепочка сертификатов в инфраструктуре открытых ключей (PKI) представляет собой последовательность X.509-сертификатов, где каждый следующий сертификат подписывает предыдущий. В классическом виде она состоит из:
При ручной верификации задача сводится к последовательной проверке подписи каждого сертификата в цепочке, начиная с конечного и заканчивая корневым.
Библиотека jsrsasign предоставляет класс
X509 для работы с сертификатами. Сертификаты могут быть
загружены в формате PEM:
const { X509 } = require('jsrsasign');
const cert = new X509();
cert.readCertPEM(pemString);
После загрузки доступны методы для извлечения:
getPublicKey)getIssuerString)getSubjectString)Каждый сертификат содержит публичный ключ, который используется для проверки подписи предыдущего сертификата:
const pubKey = cert.getPublicKey();
Для цепочки:
Метод verifySignature выполняет криптографическую
проверку:
const isValid = cert.verifySignature(pubKey);
Однако важно понимать:
cert — это проверяемый сертификатpubKey — публичный ключ издателя (issuer)Пример проверки цепочки из двух сертификатов:
const leaf = new X509();
leaf.readCertPEM(leafPem);
const intermediate = new X509();
intermediate.readCertPEM(intermediatePem);
const intermediateKey = intermediate.getPublicKey();
const valid = leaf.verifySignature(intermediateKey);
Ручная верификация требует строгого порядка:
Пример:
function verifyChain(chain) {
for (let i = 0; i < chain.length - 1; i++) {
const current = new X509();
current.readCertPEM(chain[i]);
const issuer = new X509();
issuer.readCertPEM(chain[i + 1]);
const pubKey = issuer.getPublicKey();
if (!current.verifySignature(pubKey)) {
return false;
}
}
// Проверка корневого сертификата
const root = new X509();
root.readCertPEM(chain[chain.length - 1]);
return root.verifySignature(root.getPublicKey());
}
Криптографическая проверка недостаточна без проверки логической связности цепочки. Требуется убедиться, что:
Issuer текущего сертификата совпадает с
Subject следующегоconst issuer = current.getIssuerString();
const subject = next.getSubjectString();
if (issuer !== subject) {
throw new Error("Нарушена цепочка доверия");
}
Каждый сертификат имеет поля:
notBeforenotAfterПроверка:
const now = new Date();
const notBefore = cert.getNotBefore();
const notAfter = cert.getNotAfter();
if (now < new Date(notBefore) || now > new Date(notAfter)) {
throw new Error("Сертификат недействителен по времени");
}
Некоторые критически важные проверки:
Определяет, может ли сертификат выступать как CA:
const bc = cert.getExtBasicConstraints();
if (bc.cA !== true && isIntermediate) {
throw new Error("Сертификат не является удостоверяющим центром");
}
Проверка наличия разрешения на подписание:
const ku = cert.getExtKeyUsageString();
if (!ku.includes("keyCertSign")) {
throw new Error("Нет права подписи сертификатов");
}
Даже корректная цепочка не гарантирует доверия. Корневой сертификат должен:
const trustedFingerprints = [
"AB:CD:EF:..."
];
const fingerprint = root.getFingerprint();
if (!trustedFingerprints.includes(fingerprint)) {
throw new Error("Недоверенный корневой сертификат");
}
jsrsasign не предоставляет полноценной встроенной
поддержки:
Ручная реализация возможна, но требует:
Для учебных целей чаще ограничиваются:
function validateCertificateChain(chain, trustedRoots) {
for (let i = 0; i < chain.length - 1; i++) {
const cert = new X509();
cert.readCertPEM(chain[i]);
const issuerCert = new X509();
issuerCert.readCertPEM(chain[i + 1]);
// Проверка подписи
if (!cert.verifySignature(issuerCert.getPublicKey())) {
return false;
}
// Проверка соответствия issuer/subject
if (cert.getIssuerString() !== issuerCert.getSubjectString()) {
return false;
}
// Проверка срока действия
const now = new Date();
if (now < new Date(cert.getNotBefore()) ||
now > new Date(cert.getNotAfter())) {
return false;
}
}
// Проверка корневого сертификата
const root = new X509();
root.readCertPEM(chain[chain.length - 1]);
const fingerprint = root.getFingerprint();
return trustedRoots.includes(fingerprint);
}
Ручная верификация используется:
Она даёт полный контроль над процессом проверки, но требует строгого соблюдения всех этапов, поскольку даже небольшое упрощение может привести к компрометации безопасности.