PKCS#1: RSA-ключи и подписи

PKCS#1 определяет стандартную структуру RSA-ключей и криптографических операций: форматы представления ключей, математические параметры RSA, а также правила формирования и проверки цифровых подписей. В контексте JavaScript-библиотеки jsrsasign этот стандарт реализуется через набор объектов и утилит, позволяющих работать с PEM-ключами, выполнять подпись и верификацию, а также управлять параметрами алгоритмов.

RSA-ключ в PKCS#1 включает следующие основные компоненты:

  • модуль n (modulus)
  • открытая экспонента e
  • закрытая экспонента d
  • простые числа p и q
  • вспомогательные значения dp, dq, qi

В jsrsasign эти параметры инкапсулируются в объект RSAKey или создаются автоматически при разборе PEM-структуры.


Форматы PKCS#1 и PKCS#8 в jsrsasign

RSA-ключи могут храниться в двух основных форматах:

PKCS#1 (RSA-specific) Содержит только RSA-параметры. Приватный ключ начинается с:

-----BEGIN RSA PRIVATE KEY-----

PKCS#8 (generic private key container) Универсальный формат для любых алгоритмов:

-----BEGIN PRIVATE KEY-----

jsrsasign поддерживает оба варианта через единый интерфейс:

const key = KEYUTIL.getKey(pem);

Функция автоматически определяет тип структуры и преобразует её в объект внутреннего представления RSAKey.


Разбор RSA-ключей

Внутреннее представление RSA-ключа в jsrsasign основано на объекте RSAKey:

const rsaKey = KEYUTIL.getKey(pem);

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

  • rsaKey.n — модуль
  • rsaKey.e — публичная экспонента
  • rsaKey.d — приватная экспонента (если ключ закрытый)
  • rsaKey.isPrivate — признак приватного ключа

Публичный ключ также может быть извлечён из приватного:

const pubKey = KEYUTIL.getPEM(rsaKey, "PKCS8PUB");

Генерация RSA-ключей PKCS#1

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

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

const pub = KEYUTIL.getPEM(kp.pubKeyObj);
const prv = KEYUTIL.getPEM(kp.prvKeyObj, "PKCS1PRV");

Параметры генерации:

  • алгоритм: "RSA"
  • длина ключа: 1024, 2048, 3072, 4096 бит
  • формат вывода: PKCS#1 или PKCS#8

Сгенерированный приватный ключ PKCS#1 может быть использован напрямую в криптографических операциях без дополнительной конвертации.


Цифровая подпись RSA в jsrsasign

Основной класс для работы с подписями — KJUR.crypto.Signature.

Процесс подписи включает три этапа:

  1. выбор алгоритма
  2. инициализация ключа
  3. формирование подписи

Подпись данных

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

Алгоритм SHA256withRSA включает:

  • хеширование сообщения SHA-256
  • применение RSA PKCS#1 v1.5 padding
  • шифрование хеша приватным ключом

Проверка подписи

Верификация выполняется с использованием публичного ключа:

const sig = new KJUR.crypto.Signature({ alg: "SHA256withRSA" });
sig.init(publicKeyPem);
sig.updateString("message");
const isValid = sig.verify(signature);

Проверка включает:

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

Алгоритмы хеширования и подписи

jsrsasign поддерживает комбинации RSA с различными хеш-функциями:

  • SHA1withRSA
  • SHA224withRSA
  • SHA256withRSA
  • SHA384withRSA
  • SHA512withRSA

Каждый алгоритм определяет длину хеша, влияющую на безопасность и размер подписи.

Пример выбора:

const sig = new KJUR.crypto.Signature({ alg: "SHA512withRSA" });

RSA-PSS подписи

Помимо PKCS#1 v1.5 jsrsasign поддерживает RSA-PSS — более современную схему подписи.

const sig = new KJUR.crypto.Signature({
  alg: "SHA256withRSAandMGF1",
  prvkeypem: privateKeyPem
});

Особенности RSA-PSS:

  • вероятностное заполнение (salt)
  • защита от некоторых атак на схему PKCS#1 v1.5
  • настраиваемые параметры saltLength

Работа с PEM-структурами

PEM-кодирование представляет ключи в base64 с заголовками:

-----BEGIN RSA PRIVATE KEY-----
(base64)
-----END RSA PRIVATE KEY-----

jsrsasign автоматически:

  • декодирует base64
  • разбирает ASN.1 структуру
  • преобразует в RSAKey

Обратная операция:

const pem = KEYUTIL.getPEM(rsaKey);

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

Из приватного PKCS#1 ключа можно получить публичный компонент:

const prvKey = KEYUTIL.getKey(privatePem);
const pubPem = KEYUTIL.getPEM(prvKey, "PKCS8PUB");

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


Типичные ошибки при работе с RSA PKCS#1

Некорректные сценарии обычно связаны с:

1. Несоответствие формата ключа

  • ожидание PKCS#1 при передаче PKCS#8

2. Неверный алгоритм подписи

  • подпись SHA256withRSA проверяется как SHA1withRSA

3. Повреждение PEM

  • лишние пробелы или переносы строк

4. Несовпадение кодировки данных

  • подпись над строкой UTF-8, проверка над байтами ASCII

Особенности безопасности RSA PKCS#1

PKCS#1 v1.5 остаётся широко используемым, однако имеет известные теоретические уязвимости в некоторых протоколах при неправильной реализации. В jsrsasign корректная реализация зависит от:

  • правильного выбора хеш-функции
  • корректного использования padding
  • достаточной длины ключа (не менее 2048 бит)

RSA-PSS снижает риски за счёт вероятностного заполнения и более строгой математической модели подписи.


Взаимодействие с внешними форматами ключей

jsrsasign способен работать с ключами, импортированными из OpenSSL:

openssl genrsa -out key.pem 2048
openssl rsa -in key.pem -pubout

Полученные PEM можно напрямую передавать в:

  • KEYUTIL.getKey()
  • KJUR.crypto.Signature.init()

Без дополнительных преобразований структура сохраняется в соответствии с PKCS#1 или PKCS#8 в зависимости от экспорта.