Извлечение открытого ключа из сертификата

В X.509 сертификате публичный ключ является центральной частью структуры: именно он используется для проверки цифровых подписей, установления TLS-соединений и криптографической верификации данных. В библиотеке Jsrsasign работа с сертификатами реализована через объектную модель X509 и утилиты KEYUTIL, которые позволяют извлекать и преобразовывать ключевые материалы без необходимости взаимодействия с низкоуровневыми криптографическими примитивами.

Сертификат X.509 в Jsrsasign обычно представлен в формате PEM:

-----BEGIN CERTIFICATE-----
MIIC...==
-----END CERTIFICATE-----

Для его обработки используется класс X509:

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

Метод readCertPEM выполняет парсинг структуры ASN.1, извлекая основные поля: субъект, издатель, срок действия и, что наиболее важно, публичный ключ.

Структура публичного ключа внутри сертификата

Публичный ключ в X.509 хранится в поле subjectPublicKeyInfo. В зависимости от алгоритма он может быть:

  • RSA (modulus + exponent)
  • ECDSA (кривая + точка)
  • DSA (реже используется)

Jsrsasign абстрагирует эти различия, приводя ключ к унифицированному объекту KEYUTIL.

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

Основной способ получения публичного ключа — метод getPublicKey:

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

const pubKey = cert.getPublicKey();

Результатом является объект ключа Jsrsasign, который можно использовать для:

  • проверки подписи
  • экспорта в PEM
  • преобразования в другие форматы

Использование KEYUTIL для извлечения ключа

Альтернативный и более универсальный способ — использование KEYUTIL:

const pubKey = KEYUTIL.getKey(pemString);

Если передан сертификат, функция автоматически извлекает публичный ключ из структуры X.509. Это делает KEYUTIL предпочтительным инструментом в прикладных задачах.

Преобразование публичного ключа в PEM

После извлечения ключа часто требуется его сериализация:

const pubKey = cert.getPublicKey();

const pem = KEYUTIL.getPEM(pubKey);

Результат:

-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----

Этот формат соответствует стандарту SPKI и используется в большинстве криптографических API.

Работа с RSA ключами

Если сертификат содержит RSA-ключ, объект будет иметь структуру:

  • n — модуль
  • e — экспонента

Пример доступа:

const pubKey = cert.getPublicKey();

console.log(pubKey.n.toString(16));
console.log(pubKey.e.toString(16));

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

Работа с ECDSA ключами

В случае эллиптической криптографии ключ содержит параметры кривой:

  • curveName
  • x, y координаты точки

Пример:

const pubKey = cert.getPublicKey();

console.log(pubKey.curveName);
console.log(pubKey.pubKeyHex);

Jsrsasign автоматически определяет используемую кривую (например, secp256r1).

Проверка корректности сертификата перед извлечением ключа

Перед извлечением ключа важно убедиться в корректности структуры:

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

if (!cert.getSubjectString()) {
  throw new Error("Некорректный сертификат");
}

Ошибки парсинга обычно связаны с:

  • повреждённым PEM
  • отсутствием BEGIN/END блоков
  • некорректной кодировкой Base64

Получение ключа напрямую из PEM сертификата

Jsrsasign позволяет пропустить явное создание X509:

const pubKey = KEYUTIL.getKey(pemString);

Этот метод:

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

Подходит для обработки потоков сертификатов без дополнительного анализа структуры.

Преобразование ключа для WebCrypto

В некоторых сценариях требуется совместимость с WebCrypto API:

const pubKey = cert.getPublicKey();

const spkiPem = KEYUTIL.getPEM(pubKey);

Далее PEM может быть импортирован:

crypto.subtle.importKey(
  "spki",
  pemToArrayBuffer(spkiPem),
  { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
  true,
  ["verify"]
);

Jsrsasign при этом выступает как слой извлечения и конвертации.

Валидация извлечённого ключа

После получения ключа можно проверить его свойства:

if (pubKey.type !== "RSA" && pubKey.type !== "EC") {
  throw new Error("Неизвестный тип ключа");
}

Также доступна проверка длины ключа:

console.log(pubKey.getKeyLength());

Это важно при работе с политиками безопасности (например, минимум 2048 бит для RSA).

Типичные сценарии использования извлечённого ключа

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

  • проверка JWT с X.509 цепочкой доверия
  • валидация TLS-сертификатов вручную
  • криптографическая проверка подписанных сообщений
  • интеграция с внешними PKI системами

Jsrsasign позволяет выполнять эти операции без перехода к нативным криптобиблиотекам.

Особенности внутренней реализации Jsrsasign

При вызове getPublicKey происходит:

  1. парсинг ASN.1 структуры certificate
  2. извлечение subjectPublicKeyInfo
  3. декодирование BIT STRING
  4. построение объекта KEYUTIL
  5. нормализация под RSA/ECDSA интерфейс

Это обеспечивает единообразный API для разных криптографических алгоритмов.

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

Если сертификат входит в цепочку:

const x509 = new X509();
x509.readCertPEM(pemCert);
const pubKey = x509.getPublicKey();

Публичный ключ извлекается независимо от позиции в цепочке. Однако верификация доверия требует дополнительной логики проверки issuer/subject связей, которая в Jsrsasign реализуется отдельно.

Практическое замечание по производительности

Парсинг сертификата и извлечение ключа являются относительно дорогими операциями по сравнению с использованием уже готового KEYUTIL объекта. При массовой обработке сертификатов рационально кэшировать результат:

const cache = new Map();

function getCachedKey(pem) {
  if (!cache.has(pem)) {
    cache.set(pem, KEYUTIL.getKey(pem));
  }
  return cache.get(pem);
}

Такой подход снижает нагрузку при повторных вызовах.