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

Библиотека jose в JavaScript предоставляет полный набор инструментов для работы с криптографией в стандартах JWS, JWE и JWT. Одним из фундаментальных этапов является генерация ключевых пар, используемых для подписи и шифрования.

Ключевая пара состоит из:

  • приватного ключа — используется для подписи или расшифровки
  • публичного ключа — используется для проверки подписи или шифрования

В контексте тестирования и разработки генерация ключей выполняется программно, без обращения к внешним системам сертификации.


Поддерживаемые алгоритмы

Библиотека jose поддерживает несколько типов алгоритмов:

Асимметричные алгоритмы подписи:

  • RSA: RS256, RS384, RS512, PS256, PS384, PS512
  • EC (Elliptic Curve): ES256, ES384, ES512
  • EdDSA: Ed25519, Ed448

Асимметричные алгоритмы шифрования:

  • RSA-OAEP, RSA-OAEP-256
  • ECDH-ES

Выбор алгоритма напрямую влияет на параметры генерируемой ключевой пары.


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

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

import { generateKeyPair } from 'jose';

const { publicKey, privateKey } = await generateKeyPair('RS256');

Особенности:

  • функция асинхронная
  • возвращает объекты WebCrypto (CryptoKey)
  • алгоритм задаётся строкой

Генерация RSA ключей

Пример генерации RSA-ключей с длиной 2048 бит:

const { publicKey, privateKey } = await generateKeyPair('RS256', {
  modulusLength: 2048,
});

Параметры:

  • modulusLength — длина ключа (2048, 3072, 4096)
  • publicExponent — обычно 0x10001

Практика:

  • 2048 бит — стандарт для тестирования
  • 4096 бит — для повышенной безопасности

Генерация EC ключей

const { publicKey, privateKey } = await generateKeyPair('ES256');

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

  • ES256 → P-256
  • ES384 → P-384
  • ES512 → P-521

Параметры задаются автоматически на основе алгоритма.


Генерация EdDSA ключей

const { publicKey, privateKey } = await generateKeyPair('EdDSA');

По умолчанию используется:

  • Ed25519 — быстрый и безопасный вариант

Особенности:

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

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

Сгенерированные ключи можно экспортировать в различные форматы.

Экспорт в JWK:

import { exportJWK } from 'jose';

const publicJwk = await exportJWK(publicKey);
const privateJwk = await exportJWK(privateKey);

Пример JWK:

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

Добавление идентификатора ключа:

publicJwk.kid = 'test-key-id';

Экспорт в PEM

Для интеграции с другими системами может понадобиться формат PEM:

import { exportSPKI, exportPKCS8 } from 'jose';

const publicPem = await exportSPKI(publicKey);
const privatePem = await exportPKCS8(privateKey);

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

Для тестов важно уметь восстанавливать ключи:

import { importJWK } from 'jose';

const publicKey = await importJWK(publicJwk, 'RS256');

Генерация ключей для шифрования

Для JWE используется тот же механизм:

const { publicKey, privateKey } = await generateKeyPair('RSA-OAEP-256');

или:

const { publicKey, privateKey } = await generateKeyPair('ECDH-ES');

Управление ключами в тестовой среде

Рекомендации:

  • генерировать ключи при старте тестов
  • использовать фиксированные kid для идентификации
  • хранить ключи в памяти или временных файлах
  • избегать использования production-ключей

Генерация симметричных ключей

Хотя ключевые пары — асимметричная криптография, библиотека поддерживает и симметричные ключи:

import { generateSecret } from 'jose';

const secret = await generateSecret('HS256');

Используется для:

  • HMAC подписи (HS256)
  • шифрования (A256GCM)

Пример полной генерации и экспорта

import {
  generateKeyPair,
  exportJWK,
  exportPKCS8,
  exportSPKI
} from 'jose';

const { publicKey, privateKey } = await generateKeyPair('RS256', {
  modulusLength: 2048,
});

const publicJwk = await exportJWK(publicKey);
const privateJwk = await exportJWK(privateKey);

const publicPem = await exportSPKI(publicKey);
const privatePem = await exportPKCS8(privateKey);

publicJwk.kid = 'demo-key';

Производительность и ограничения

RSA:

  • медленная генерация
  • высокая нагрузка на CPU

EC:

  • быстрее RSA
  • меньший размер ключей

EdDSA:

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

Безопасность при тестировании

Даже тестовые ключи требуют аккуратного обращения:

  • не использовать слабые параметры (например, RSA < 2048)
  • не публиковать приватные ключи
  • очищать временные данные после тестов

Асинхронная природа WebCrypto

Внутри jose используется WebCrypto API, поэтому:

  • все операции генерации — асинхронные
  • важно корректно обрабатывать await

Интеграция с Node.js и браузером

Библиотека работает:

  • в Node.js (через crypto)
  • в браузерах (через SubtleCrypto)

Код генерации ключей одинаков в обеих средах.


Повторяемость тестов

Для unit-тестирования иногда требуется детерминированность:

  • jose не поддерживает seed для генерации
  • рекомендуется сохранять заранее сгенерированные ключи

Частые ошибки

Неправильный алгоритм:

await generateKeyPair('HS256'); // ошибка

HS256 — симметричный алгоритм


Отсутствие параметров RSA:

await generateKeyPair('RS256'); // иногда требует modulusLength

Неверный импорт:

import { generateKeyPair } from 'jose'; // корректно

Организация ключей в проекте

Структура для тестов:

/keys
  public.jwk.json
  private.jwk.json
/tests
  auth.test.js

Или динамическая генерация:

beforeAll(async () => {
  keys = await generateKeyPair('ES256');
});

Использование с JWT

После генерации ключей:

import { SignJWT } from 'jose';

const jwt = await new SignJWT({ sub: '123' })
  .setProtectedHeader({ alg: 'RS256' })
  .sign(privateKey);

Совместимость форматов

jose обеспечивает конвертацию между:

  • JWK
  • PEM
  • CryptoKey

Это позволяет:

  • интегрироваться с OpenSSL
  • использовать ключи из внешних систем

Расширенные параметры генерации

Некоторые алгоритмы поддерживают дополнительные настройки:

await generateKeyPair('PS256', {
  modulusLength: 3072,
});

Жизненный цикл ключей в тестах

  1. Генерация
  2. Использование (подпись/проверка)
  3. Экспорт (при необходимости)
  4. Удаление/очистка

Минимальный рабочий пример

import { generateKeyPair, exportJWK } from 'jose';

(async () => {
  const { publicKey, privateKey } = await generateKeyPair('ES256');

  console.log(await exportJWK(publicKey));
})();

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

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

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