Построение простого PKI на JavaScript

PKI (Public Key Infrastructure) в JavaScript реализуется через сочетание криптографических примитивов и форматов сертификатов X.509, где библиотека Jsrsasign обеспечивает полный набор средств для генерации ключей, создания сертификатов, подписи и проверки цепочек доверия.

Базовая модель PKI включает три уровня:

  • корневой центр сертификации (Root CA)
  • промежуточные центры сертификации (Intermediate CA)
  • конечные сертификаты (End Entity Certificates)

Корневой сертификат является доверенной точкой, от которой строится вся цепочка доверия. Его приватный ключ используется только для подписи промежуточных CA. Нарушение безопасности Root CA приводит к компрометации всей инфраструктуры, поэтому в реальных системах он обычно хранится офлайн.

Промежуточные CA уменьшают риск использования корневого ключа и позволяют разделять зоны ответственности. Конечные сертификаты применяются для TLS-серверов, клиентской аутентификации, подписи данных.

Возможности Jsrsasign для PKI

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

  • генерация RSA и EC ключей
  • создание X.509 сертификатов
  • работа с ASN.1 структурами
  • цифровая подпись (RSA, ECDSA, HMAC)
  • проверка сертификатов и цепочек
  • обработка PEM/DER форматов

Основные пространства имен:

  • KEYUTIL — генерация и импорт ключей
  • KJUR.crypto — криптографические операции
  • KJUR.asn1.x509 — работа с сертификатами
  • X509 — высокоуровневое API сертификатов

Генерация ключевой пары

Создание RSA ключей выполняется через генератор ключевых пар:

var kp = KEYUTIL.generateKeypair("RSA", 2048);

var privateKey = kp.prvKeyObj;
var publicKey = kp.pubKeyObj;

Приватный ключ используется для подписи сертификатов, публичный — встраивается в сертификаты.

Для более строгих конфигураций применяется длина 3072 или 4096 бит.

Создание корневого сертификата (Root CA)

Корневой сертификат самоподписанный, то есть подписывается собственным приватным ключом.

var caKey = KEYUTIL.generateKeypair("RSA", 2048);

var caCert = new X509();

caCert.setSerialNumberByParam({ int: 1 });

caCert.setIssuerName("CN=RootCA,O=Example,C=US");
caCert.setSubjectName("CN=RootCA,O=Example,C=US");

caCert.setNotBefore("20250101000000Z");
caCert.setNotAfter("20350101000000Z");

caCert.setPublicKey(caKey.pubKeyObj);

caCert.sign(caKey.prvKeyObj, "sha256WithRSAEncryption");

var caPem = caCert.getPEM();

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

  • Issuer и Subject совпадают
  • подпись выполняется приватным ключом того же субъекта
  • срок действия обычно длительный

Создание промежуточного CA

Intermediate CA подписывается Root CA и формирует доверенную цепочку.

var intKey = KEYUTIL.generateKeypair("RSA", 2048);

var intCert = new X509();

intCert.setSerialNumberByParam({ int: 2 });

intCert.setIssuerName("CN=RootCA,O=Example,C=US");
intCert.setSubjectName("CN=IntermediateCA,O=Example,C=US");

intCert.setNotBefore("20250101000000Z");
intCert.setNotAfter("20300101000000Z");

intCert.setPublicKey(intKey.pubKeyObj);

intCert.sign(caKey.prvKeyObj, "sha256WithRSAEncryption");

var intPem = intCert.getPEM();

На этом уровне формируется разделение доверия: Root CA используется только для подписи Intermediate CA.

Выпуск конечного сертификата

Конечный сертификат используется для TLS или подписи данных.

var endKey = KEYUTIL.generateKeypair("RSA", 2048);

var endCert = new X509();

endCert.setSerialNumberByParam({ int: 3 });

endCert.setIssuerName("CN=IntermediateCA,O=Example,C=US");
endCert.setSubjectName("CN=service.example.com,O=Example,C=US");

endCert.setNotBefore("20250101000000Z");
endCert.setNotAfter("20260101000000Z");

endCert.setPublicKey(endKey.pubKeyObj);

endCert.sign(intKey.prvKeyObj, "sha256WithRSAEncryption");

var endPem = endCert.getPEM();

Здесь Issuer совпадает с Intermediate CA, формируя цепочку:

Root CA → Intermediate CA → End Entity

Построение цепочки доверия

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

  1. End certificate проверяется ключом Intermediate CA
  2. Intermediate CA проверяется Root CA
  3. Root CA считается доверенным якорем

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

var cert = new X509();
cert.readCertPEM(endPem);

var pubKey = intKey.pubKeyObj;

var isValid = cert.verifySignature(pubKey);

Для полной цепочки проверка выполняется каскадно.

Подпись данных с использованием PKI

PKI используется не только для TLS, но и для цифровых подписей.

var sig = new KJUR.crypto.Signature({ alg: "SHA256withRSA" });

sig.init(endKey.prvKeyObj);

sig.updateString("critical data");

var signatureHex = sig.sign();

Проверка:

var verifier = new KJUR.crypto.Signature({ alg: "SHA256withRSA" });

verifier.init(endKey.pubKeyObj);

verifier.updateString("critical data");

var result = verifier.verify(signatureHex);

Работа с PEM и DER форматами

Jsrsasign поддерживает преобразования:

  • PEM → internal objects
  • DER → PEM
  • Base64 encoding

Пример загрузки сертификата:

var cert = new X509();
cert.readCertPEM(pemString);

Извлечение публичного ключа:

var pubKey = cert.getPublicKey();

Расширения сертификатов (X.509 Extensions)

Расширения определяют назначение сертификата:

  • Basic Constraints (CA: true/false)
  • Key Usage (digitalSignature, keyCertSign)
  • Subject Alternative Name (SAN)

Пример установки расширений:

cert.setExtBasicConstraints({ cA: true, critical: true });

cert.setExtKeyUsage(["keyCertSign", "cRLSign"]);

Для конечных сертификатов:

cert.setExtKeyUsage(["digitalSignature", "keyEncipherment"]);

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

Проверка роли сертификата осуществляется через расширения:

  • CA сертификаты имеют cA=true
  • конечные сертификаты — cA=false

Это важно при построении цепочки доверия, чтобы исключить использование end-entity сертификата как промежуточного центра.

Интеграция PKI в веб-приложения

PKI на Jsrsasign часто используется для:

  • клиентской аутентификации без паролей
  • подписи API-запросов
  • проверки целостности данных
  • реализации JWT-подобных механизмов с RSA

Пример проверки подписи JWT-подобного токена:

var sig = new KJUR.crypto.Signature({ alg: "SHA256withRSA" });

sig.init(pubKey);

sig.updateString(header + "." + payload);

var verified = sig.verify(signature);

Управление сроками и отзыв сертификатов

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

  • NotBefore — начало действия
  • NotAfter — окончание действия

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

var now = new Date();

var notBefore = cert.getNotBefore();
var notAfter = cert.getNotAfter();

var isActive = (now >= notBefore && now <= notAfter);

Отзыв сертификатов в простых системах моделируется через CRL (Certificate Revocation List), который можно реализовать вручную как список серийных номеров.

Формирование минимальной PKI-системы

Полноценная минимальная схема включает:

  • Root CA (самоподписанный)
  • Intermediate CA (подписан Root)
  • End Entity сертификаты (подписаны Intermediate)
  • проверку цепочки доверия через публичные ключи
  • контроль сроков действия
  • управление расширениями X.509

Jsrsasign позволяет реализовать эту модель полностью в JavaScript без внешних криптографических сервисов, опираясь на встроенные ASN.1 структуры и криптографические алгоритмы RSA и ECDSA