Проверка TLS-сертификатов вручную в JavaScript с использованием Jsrsasign сводится к разбору X.509-структуры, построению цепочки доверия и криптографической проверке подписи каждого звена. В отличие от встроенной в браузеры и Node.js автоматической валидации, здесь все этапы выполняются явно, что позволяет контролировать процесс на уровне протокола и сертификатов.
TLS-сертификат в основе всегда является X.509-объектом, содержащим:
В Jsrsasign работа с такими объектами осуществляется через модуль
X509.
import { X509 } from "jsrsasign";
const x509 = new X509();
x509.readCertPEM(pemCert);
После парсинга доступ к структуре осуществляется через внутренний ASN.1-разбор.
Первый шаг — извлечение базовых данных сертификата сервера.
const cert = new X509();
cert.readCertPEM(serverPem);
const subject = cert.getSubjectString();
const issuer = cert.getIssuerString();
const notBefore = cert.getNotBefore();
const notAfter = cert.getNotAfter();
Критически важные проверки на этом этапе:
Современные TLS-соединения игнорируют CN и используют SAN.
function checkHostname(cert, hostname) {
const sans = cert.getExtSubjectAltName();
if (sans && sans.includes(hostname)) return true;
const cn = cert.getSubjectString().match(/CN=([^,]+)/);
return cn && cn[1] === hostname;
}
В реальной криптографической проверке SAN обязателен, CN используется только как fallback.
TLS-сертификат редко является самоподписанным. Обычно цепочка выглядит так:
Server Certificate → Intermediate CA → Root CA
Каждое звено подписано предыдущим.
Для ручной проверки требуется набор доверенных корневых сертификатов:
const trustedRoots = [rootPem1, rootPem2];
const intermediates = [intermediatePem1, intermediatePem2];
Jsrsasign предоставляет возможность криптографической проверки подписи через публичный ключ издателя.
const issuerCert = new X509();
issuerCert.readCertPEM(intermediatePem);
const pubKey = issuerCert.getPublicKey();
const isValid = cert.verifySignature(pubKey);
Этот шаг подтверждает, что сертификат действительно был подписан указанным издателем и не изменён.
Цепочка проверяется итеративно:
function verifyChain(certPem, intermediates, roots) {
const cert = new X509();
cert.readCertPEM(certPem);
let current = cert;
for (const interm of intermediates) {
const issuer = new X509();
issuer.readCertPEM(interm);
const ok = current.verifySignature(issuer.getPublicKey());
if (ok) {
current = issuer;
}
}
for (const root of roots) {
const rootCert = new X509();
rootCert.readCertPEM(root);
if (current.verifySignature(rootCert.getPublicKey())) {
return true;
}
}
return false;
}
Определяет, является ли сертификат CA.
const isCA = cert.getExtBasicConstraints().isCA;
Если серверный сертификат имеет CA=true, это нарушение
структуры TLS.
Ограничивает назначение ключа:
const keyUsage = cert.getExtKeyUsage();
Для серверных сертификатов обязательно наличие
digitalSignature или keyEncipherment.
const now = new Date();
const valid =
now >= new Date(cert.getNotBefore()) &&
now <= new Date(cert.getNotAfter());
Даже корректная цепочка становится недействительной при истёкшем сроке.
Внутри Jsrsasign проверка подписи зависит от алгоритма:
Пример RSA-валидации:
const pubKey = issuerCert.getPublicKey();
const verified = cert.verifySignature(pubKey);
Фактически происходит:
SAN хранится в расширениях сертификата:
const sans = cert.getExtSubjectAltName();
Пример содержимого:
DNS:www.example.com
DNS:example.com
Проверка требует строгого соответствия домена:
function matchSAN(sans, host) {
return sans.split(",").some(s => s.includes(host));
}
Процесс в терминах шагов:
const server = new X509();
server.readCertPEM(serverPem);
const now = Date.now();
const validTime =
now >= Date.parse(server.getNotBefore()) &&
now <= Date.parse(server.getNotAfter());
const hostnameOK = checkHostname(server, "example.com");
const intermediate = new X509();
intermediate.readCertPEM(intermediatePem);
const sigOK = server.verifySignature(intermediate.getPublicKey());
const root = new X509();
root.readCertPEM(rootPem);
const trusted = intermediate.verifySignature(root.getPublicKey());
Самоподписанный сертификат — это случай, когда Issuer == Subject.
const isSelfSigned =
cert.getIssuerString() === cert.getSubjectString();
Проверка подписи выполняется публичным ключом самого сертификата:
const ok = cert.verifySignature(cert.getPublicKey());
Такой сертификат принимается только при наличии явного доверия.
function validateTLS(serverPem, intermPem, rootPem, host) {
const server = new X509();
server.readCertPEM(serverPem);
if (!checkHostname(server, host)) return false;
if (!verifyChain(serverPem, intermPem, rootPem)) return false;
const now = Date.now();
if (
now < Date.parse(server.getNotBefore()) ||
now > Date.parse(server.getNotAfter())
) return false;
return true;
}