Алгоритмы управления ключом

Управление криптографическими ключами в Jsrsasign строится вокруг унифицированного слоя KEYUTIL, который абстрагирует различия между RSA, EC и различными форматами представления ключей. Ключевая задача библиотеки — обеспечить взаимную конвертацию, генерацию, парсинг и безопасную работу с ключевыми материалами без привязки к низкоуровневым API WebCrypto.


Представления криптографических ключей

В экосистеме Jsrsasign один и тот же ключ может существовать в нескольких формах:

PEM-формат

Наиболее распространённый текстовый формат, основанный на Base64 с заголовками:

  • -----BEGIN PRIVATE KEY-----
  • -----BEGIN RSA PRIVATE KEY-----
  • -----BEGIN PUBLIC KEY-----

PEM используется для хранения и передачи ключей между системами и является основным форматом взаимодействия в Jsrsasign.

DER-формат

Бинарное представление ключа. В Jsrsasign встречается реже, обычно при работе с внешними криптографическими системами.

PKCS#1 и PKCS#8

  • PKCS#1 — специализированный формат RSA-ключей
  • PKCS#8 — универсальный формат для приватных ключей различных алгоритмов

PKCS#8 является предпочтительным для универсальной обработки в Jsrsasign.

JWK (JSON Web Key)

JSON-структура, используемая в современных web-протоколах:

{
  "kty": "RSA",
  "n": "...",
  "e": "AQAB",
  "d": "..."
}

Jsrsasign поддерживает преобразование JWK ↔︎ PEM.


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

Генерация ключей выполняется через KEYUTIL.generateKeypair.

RSA ключи

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

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

Параметры:

  • RSA — алгоритм
  • 2048 / 3072 / 4096 — длина ключа

Генерация RSA включает создание простых чисел, вычисление модуля n, открытой экспоненты e и закрытой экспоненты d.


EC (Elliptic Curve) ключи

const kp = KEYUTIL.generateKeypair("EC", "secp256r1");

Используемые кривые:

  • secp256r1 (P-256)
  • secp384r1
  • secp521r1

EC-ключи компактнее RSA и чаще используются в современных протоколах (JWT, TLS 1.3).


Импорт ключей

Загрузка PEM

const key = KEYUTIL.getKey(pemString);

KEYUTIL.getKey автоматически определяет тип ключа:

  • RSA private/public
  • EC key
  • PKCS#8 wrapper

Импорт JWK

const key = KEYUTIL.getKey(jwkObject);

При этом происходит:

  • декодирование Base64URL параметров
  • восстановление математической структуры ключа
  • нормализация к внутреннему представлению RSAKey или KJUR.crypto.ECDSA

Обработка защищённых ключей

const key = KEYUTIL.getKey(pemEncrypted, "password");

Поддерживаются:

  • AES-128-CBC / AES-256-CBC
  • 3DES (legacy)
  • PBKDF2 / EVP KDF (в зависимости от формата)

Экспорт ключей

PEM экспорт

const pem = KEYUTIL.getPEM(privateKeyObj, "PKCS8PRV");

Типы экспорта:

  • "PKCS8PRV" — приватный ключ PKCS#8
  • "PKCS1PRV" — RSA-specific
  • "SPKI" — публичный ключ

Экспорт в JWK

const jwk = KEYUTIL.getJWK(privateKeyObj);

Используется в web-API и OAuth2 / JWT инфраструктуре.


Внутреннее представление ключей

Jsrsasign преобразует внешние форматы в внутренние структуры:

RSAKey

Содержит:

  • n — модуль
  • e — публичная экспонента
  • d — приватная экспонента
  • p, q — простые множители
  • dp, dq, qi — CRT-параметры

CRT ускоряет операции подписи и расшифровки.


ECKey

Содержит:

  • curve
  • pub — точка на кривой
  • priv — скалярное значение

Проверка и валидация ключей

Проверка структуры

KEYUTIL.getKey(pem);

Если ключ некорректен, выбрасывается исключение.


Проверка соответствия пары

const sig = new KJUR.crypto.Signature({ "alg": "SHA256withRSA" });
sig.init(privateKey);
sig.updateString("data");
const signature = sig.sign();

const pub = KEYUTIL.getKey(publicKeyPem);
const verifier = new KJUR.crypto.Signature({ "alg": "SHA256withRSA" });
verifier.init(pub);
verifier.updateString("data");

const isValid = verifier.verify(signature);

Хэширование ключей и отпечатки

Для идентификации ключей используется fingerprint:

const fp = KEYUTIL.getKey(publicKeyPem).getPublicKey().getFingerprint();

Алгоритмы:

  • SHA-1 (устаревший, но часто используется в SSH-совместимых сценариях)
  • SHA-256 (современный стандарт)

Fingerprint применяется для:

  • сравнения ключей
  • хранения идентификаторов
  • проверки подмены ключа

Ротация ключей

Алгоритмическая ротация ключей включает:

Параллельное существование версий

  • key_v1
  • key_v2

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

const keys = [key1, key2];

for (const k of keys) {
  if (verifyWith(k)) break;
}

Миграция подписей

Поддерживается стратегия:

  • старые подписи валидируются старым ключом
  • новые создаются новым ключом

Управление жизненным циклом ключей

Генерация

  • выбор алгоритма
  • выбор длины
  • выбор кривой (EC)

Хранение

Jsrsasign не навязывает storage layer, но ключи часто:

  • сериализуются в PEM
  • сохраняются в защищённые хранилища браузера
  • хранятся в переменных окружения на сервере

Загрузка

const key = KEYUTIL.getKey(process.env.PRIVATE_KEY);

Уничтожение в памяти

JavaScript не предоставляет гарантированного удаления памяти, поэтому:

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

Преобразование ключевых форматов

PEM ↔︎ JWK

const jwk = KEYUTIL.getJWK(KEYUTIL.getKey(pem));
const pem2 = KEYUTIL.getPEM(KEYUTIL.getKey(jwk));

RSA ↔︎ EC (ограничение)

Прямое преобразование невозможно, но возможно:

  • создание нового ключа
  • перенос только логики подписи

Работа с криптографическими алгоритмами подписи

Ключи тесно связаны с алгоритмами:

  • SHA256withRSA
  • SHA512withRSA
  • SHA256withECDSA
const sig = new KJUR.crypto.Signature({
  alg: "SHA256withECDSA"
});
sig.init(ecPrivateKey);

Безопасность при управлении ключами

Критические аспекты:

Избегание утечек

  • не логируются приватные ключи
  • PEM не выводится в консоль в production

Контроль экспорта

KEYUTIL.getPEM(privateKeyObj, "PKCS8PRV");

Экспорт приватных ключей должен быть ограничен.


Работа с энтропией

Генерация ключей RSA/EC требует криптографически стойкой энтропии, предоставляемой средой выполнения (Node.js или браузер).


Использование ключей в инфраструктуре JWT

Jsrsasign часто применяется в JWT-сценариях:

const token = KJUR.jws.JWS.sign(
  "RS256",
  header,
  payload,
  privateKey
);

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

const isValid = KJUR.jws.JWS.verify(token, publicKey, ["RS256"]);

Кэширование и повторное использование ключей

Для повышения производительности:

  • ключи парсятся один раз
  • объект RSAKey сохраняется
  • PEM используется только на этапе загрузки

Совместимость с внешними системами

Jsrsasign ключи совместимы с:

  • OpenSSL
  • WebCrypto API (через JWK)
  • Java KeyStore (через PEM экспорт)
  • JWT/OAuth2 серверными библиотеками