RSA-PSS: параметры saltLength и отличия от PKCS1

RSA-PSS в Web Crypto API реализуется через алгоритм "RSA-PSS" и относится к вероятностным схемам подписи, где каждый вызов подписания использует случайную соль. Это ключевое отличие от RSASSA-PKCS1-v1_5, где подпись формируется детерминированно на основе входного сообщения и приватного ключа.

В Web Crypto API алгоритм RSA-PSS задаётся через объект конфигурации:

const algorithm = {
  name: "RSA-PSS",
  saltLength: 32
};

Этот объект используется как при подписании, так и при проверке подписи:

crypto.subtle.sign(
  {
    name: "RSA-PSS",
    saltLength: 32
  },
  privateKey,
  data
);
crypto.subtle.verify(
  {
    name: "RSA-PSS",
    saltLength: 32
  },
  publicKey,
  signature,
  data
);

Критически важно, что параметры sign и verify должны совпадать, включая saltLength, иначе проверка подписи завершится неудачей.


Параметр saltLength

saltLength определяет длину случайной соли (salt), используемой внутри схемы PSS при формировании подписи.

Основные свойства saltLength:

  • измеряется в байтах
  • влияет на степень случайности подписи
  • напрямую участвует в структуре EMSA-PSS encoding
  • должен быть согласован между подписыванием и проверкой

Чаще всего применяются следующие значения:

  • 32 байта — для SHA-256
  • 48 байт — для SHA-384
  • 64 байта — для SHA-512

Это не строгие требования стандарта WebCrypto, но общепринятая практика, основанная на рекомендациях RFC 8017.


Роль saltLength в криптографической стойкости

RSA-PSS использует схему, в которой сообщение дополняется случайной солью перед возведением в степень по модулю RSA.

Упрощённо процесс выглядит так:

  1. Вычисляется хеш сообщения
  2. Генерируется случайная соль длиной saltLength
  3. Формируется закодированная структура (EMSA-PSS)
  4. Производится RSA-операция с приватным ключом

Соль делает каждую подпись уникальной даже при одинаковом входном сообщении. Это устраняет один из фундаментальных недостатков PKCS#1 v1.5 — детерминированность.


Отличия RSA-PSS и RSASSA-PKCS1-v1_5

1. Детерминированность

PKCS1 v1.5:

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

PSS:

  • подпись всегда различается
  • используется криптографическая случайность (salt)

2. Безопасность

RSA-PSS считается криптографически более устойчивым:

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

PKCS1 v1.5:

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

3. Структура padding

PKCS#1 v1.5:

EM = 0x00 || 0x01 || PS || 0x00 || DigestInfo

где PS — фиксированная последовательность байтов.

PSS:

EM = maskedDB || H || 0xbc

где maskedDB включает соль, маску и служебные биты.


4. Гибкость и параметры

RSA-PSS в WebCrypto предоставляет явный параметр:

  • saltLength — управляемый разработчиком

В PKCS1 v1.5 таких параметров нет.


Практическое влияние saltLength

Выбор saltLength влияет на:

Совместимость

Если сервер ожидает:

saltLength: 32

а клиент использует:

saltLength: 16

подписи будут несовместимы.


Производительность

Большая соль:

  • увеличивает размер внутренних вычислений
  • незначительно влияет на производительность, но в массовых операциях может быть заметно

Безопасность

Общее правило:

  • saltLength = hashLength

Примеры:

  • SHA-256 → 32
  • SHA-384 → 48
  • SHA-512 → 64

Использование меньших значений допустимо, но снижает криптографический запас стойкости.


Web Crypto API: нюансы реализации RSA-PSS

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

const keyPair = await crypto.subtle.generateKey(
  {
    name: "RSA-PSS",
    modulusLength: 2048,
    publicExponent: new Uint8Array([1, 0, 1]),
    hash: "SHA-256"
  },
  true,
  ["sign", "verify"]
);

Важно, что hash задаётся на уровне ключа, а saltLength — на уровне операции.


Подписание

const signature = await crypto.subtle.sign(
  {
    name: "RSA-PSS",
    saltLength: 32
  },
  keyPair.privateKey,
  messageBuffer
);

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

const isValid = await crypto.subtle.verify(
  {
    name: "RSA-PSS",
    saltLength: 32
  },
  keyPair.publicKey,
  signature,
  messageBuffer
);

Типичные ошибки при использовании saltLength

Несовпадение параметров

Одна из наиболее частых проблем:

  • подпись создаётся с saltLength: 32
  • проверка выполняется с saltLength: 0 или другим значением

Результат — всегда false.


Несоответствие hash и saltLength

Хотя WebCrypto не требует строгого соответствия, несогласованность приводит к снижению безопасности и проблемам совместимости с внешними системами.


Попытка использовать PKCS1 параметры

RSA-PSS и RSASSA-PKCS1-v1_5 — разные алгоритмы:

name: "RSA-PSS"   // требует saltLength
name: "RSASSA-PKCS1-v1_5" // saltLength не используется

Смешивание параметров приводит к ошибкам выполнения.


Сравнение поведения в реальных сценариях

Подпись API-запросов

RSA-PSS предпочтителен:

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

PKCS1 v1.5 встречается в legacy-системах:

  • банковские протоколы
  • старые PKI-инфраструктуры

Валидация сертификатов

RSA-PSS постепенно становится стандартом:

  • современные TLS-конфигурации поддерживают PSS
  • PKCS1 v1.5 остаётся для обратной совместимости

Влияние WebCrypto на выбор схемы

Web Crypto API фактически фиксирует современный криптографический подход:

  • RSA-PSS рассматривается как предпочтительный вариант
  • PKCS#1 v1.5 оставлен для совместимости

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