Перейти к содержимому
ecdsa.com

RSA-PSS или PKCS#1 v1.5: какое дополнение для подписи RSA

Те же ключи RSA, те же размеры, та же скорость. У PSS есть доказательство стойкости, и именно PSS требуется для рукопожатий TLS 1.3; PKCS#1 v1.5 используется повсюду, и при корректной проверке практических атак на него нет.
Что сравниваетсяRSASSA-PSSRSASSA-PKCS1-v1_5

Выбор между RSA-PSS и PKCS#1 v1.5 — это выбор того, как кодируется хеш перед операцией RSA с закрытым ключом. Обе схемы описаны в PKCS #1 (RFC 8017), обе одобрены в FIPS 186-5 и обе работают с одной и той же парой ключей RSA. Различаются они обоснованием стойкости, детерминированностью и параметрами, которые должны совпадать у подписанта и проверяющей стороны.

Коротко о главном

RSASSA-PSSRSASSA-PKCS1-v1_5
СпецификацияRFC 8017, § 8.1, кодирование EMSA-PSS, § 9.1RFC 8017, § 8.2, кодирование EMSA-PKCS1-v1_5, § 9.2
КодированиеХеш с солью, маскированный с помощью MGF10x00 01 FF…FF 00 ‖ DigestInfo
РандомизацияДа (если длина соли не равна 0)Нет: один и тот же ключ и одно и то же сообщение дают одну и ту же подпись
Доказательство стойкостиТочная редукция к задаче RSA в модели случайного оракулаСопоставимого доказательства нет; практических атак на корректную проверку нет
Параметры, которые нужно согласоватьХеш, хеш для MGF, длина соли, поле trailerТолько хеш
Размер подписиРазмер модуля (256 байт для RSA-2048)Такой же
СкоростьТа же операция RSAТакая же
JOSEPS256, PS384, PS512 (длина соли = длина хеша)RS256, RS384, RS512
TLS 1.3Требуется для CertificateVerify с RSA (rsa_pss_rsae_sha256 0x0804, rsa_pss_pss_sha256 0x0809)Только для подписей внутри сертификатов (rsa_pkcs1_sha256 0x0401)
X.509id-RSASSA-PSS 1.2.840.113549.1.1.10 с параметрамиsha256WithRSAEncryption 1.2.840.113549.1.1.11
Ограничение FIPS 186-5Длина соли от 0 до длины хешаПроверки DigestInfo в § 5.4(h)

Различия в стойкости

Дополнение подписей PKCS#1 v1.5 появилось в 1991 году. Оно детерминировано, и у него никогда не было такого же чистого доказательства стойкости, как у PSS, но и спустя три десятилетия нет практического способа подделать подпись так, чтобы её приняла проверка, сверяющая весь закодированный блок. Реальные взломы были ошибками реализации: атака Блейхенбахера 2006 года позволяла подделывать подписи для открытой экспоненты 3, если парсер игнорировал лишние байты после DigestInfo, а разновидности этой ошибки потом снова обнаруживались в библиотеках, например в NSS (BERserk, 2014), и в других. RFC 8017, § 8.2.2, исключает весь этот класс ошибок: ожидаемый блок кодируется заново и сравнивается побайтно, а FIPS 186-5, § 5.4(h), закрепляет эти проверки как требования.

RSASSA-PSS (Белларе и Рогуэй; стандартизирована в PKCS #1 v2.1 в 2002 году) хеширует случайную соль вместе с хешем сообщения и маскирует результат с помощью MGF1. Её стойкость точно сводится к задаче RSA в модели случайного оракула, а благодаря рандомизации сбой или утечка по побочному каналу при вычислении одной подписи раскрывает меньше. С солью нулевой длины PSS становится детерминированной, но сохраняет доказательство при несколько иных допущениях.

Не путайте ни одну из них с дополнением PKCS #1 v1.5 для шифрования: оно действительно взломано атаками на оракул дополнения (padding oracle: атака Блейхенбахера 1998 года, ROBOT 2017 года) и удалено из TLS 1.3. Дополнение для подписи и дополнение для шифрования — разные схемы с общим названием.

Совместимость: ловушка с длиной соли

Для PSS подписант и проверяющая сторона должны договориться о трёх параметрах: хеше, хеше для MGF1 и длине соли. JOSE фиксирует все три (PS256 означает SHA-256, MGF1-SHA-256 и соль длиной 32 байта), а TLS 1.3 требует, чтобы длина соли была равна длине хеша. В остальных случаях это нужно проверять: OpenSSL 1.1.1 и 3.0 по умолчанию подписывали с максимальной длиной соли (222 байта для RSA-2048 с SHA-256), а OpenSSL 3.1 и новее по умолчанию используют auto-digestmax, что даёт соль размером с дайджест. Поэтому проверяющая сторона, настроенная на фиксированную длину соли, отвергает подписи, созданные другим поколением OpenSSL.

Скрипт ниже, запущенный с OpenSSL 3.6.5, демонстрирует обе особенности.

OpenSSL: детерминизм и проверка длины соли в PSS
#!/bin/sh
dir=$(mktemp -d)
cd "$dir" || exit 1
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out key.pem 2>/dev/null
openssl pkey -in key.pem -pubout -out pub.pem
printf 'hello' > msg

openssl dgst -sha256 -sign key.pem -out v15a.sig msg
openssl dgst -sha256 -sign key.pem -out v15b.sig msg
cmp -s v15a.sig v15b.sig && echo "PKCS#1 v1.5: identical signatures" || echo "PKCS#1 v1.5: different signatures"

openssl dgst -sha256 -sign key.pem -sigopt rsa_padding_mode:pss -out pssa.sig msg
openssl dgst -sha256 -sign key.pem -sigopt rsa_padding_mode:pss -out pssb.sig msg
cmp -s pssa.sig pssb.sig && echo "PSS: identical signatures" || echo "PSS: different signatures"

for saltlen in digest max; do
  printf 'verify PSS with rsa_pss_saltlen:%s -> ' "$saltlen"
  openssl dgst -sha256 -verify pub.pem -sigopt rsa_padding_mode:pss \
    -sigopt "rsa_pss_saltlen:$saltlen" -signature pssa.sig msg 2>&1 | grep -o -E "Verified OK|salt length check failed"
done
cd / && rm -rf "$dir"
Результат
PKCS#1 v1.5: identical signatures
PSS: different signatures
verify PSS with rsa_pss_saltlen:digest -> Verified OK
verify PSS with rsa_pss_saltlen:max -> salt length check failed

Проверяющая сторона с настройкой auto определяет длину соли по самой подписи — это самый совместимый вариант, если подписант вам неподконтролен.

Ключи и сертификаты

Один и тот же ключ RSA может создавать подписи обоих видов, а ключ rsaEncryption в сертификате годится для обоих (в TLS это называется rsa_pss_rsae). В X.509 есть также ключи id-RSASSA-PSS, параметры которых допускают только схему PSS (rsa_pss_pss в TLS); такие ключи нельзя использовать в другой схеме, но многие инструменты и некоторые браузеры плохо с ними работают, поэтому на практике выбирают ключи rsaEncryption, которые подписывают по PSS. Почти все подписи сертификатов в WebPKI — это PKCS#1 v1.5 или ECDSA, и уже поэтому проверяющие стороны обязаны поддерживать v1.5.

Что когда выбирать

  • Рукопожатия TLS 1.3: PSS обязательна; ваша библиотека автоматически использует её с любым сертификатом RSA.
  • JWT: PS256, если его поддерживают все получатели токенов, иначе RS256; а ещё лучше рассмотрите ES256 (см. ES256 или RS256).
  • Сертификаты X.509 и CRL, которые вы выпускаете: PKCS#1 v1.5 (sha256WithRSAEncryption) ради максимальной совместимости, если только в закрытой экосистеме стандартом не принята PSS.
  • Новые подписи документов, прошивок или API с ключами RSA: PSS с длиной соли, равной длине хеша.
  • Совместимость с HSM и облачными KMS: поддерживаются обе схемы (например, RSASSA_PSS_SHA_256 и RSASSA_PKCS1_V1_5_SHA_256 в AWS KMS); ориентируйтесь на то, что поддерживает проверяющая сторона.
  • Проверка подписей любого вида: используйте высокоуровневую функцию проверки из библиотеки и никогда не полагайтесь на ручное декодирование закодированного сообщения.

Заметки о миграции

Для перехода с v1.5 на PSS новые ключи не нужны, поэтому его легко провести поэтапно.

  1. Сначала научите проверяющие стороны работать с PSS: в JOSE разрешите для ключа PS256 (правило «один алгоритм на ключ» по-прежнему действует, поэтому опубликуйте тот же ключевой материал под новым kid с alg, равным PS256, или проведите ротацию).
  2. Задайте параметры явно: хеш SHA-256, MGF1 SHA-256, длина соли 32 байта; не полагайтесь на значения по умолчанию в библиотеках, которые меняются от версии к версии.
  3. Переключите подписантов и следите за ошибками проверки, в которых упоминается длина соли или дополнение.
  4. Перестаньте принимать v1.5 для этих ключей, когда истечёт срок действия старых подписей, и оставьте v1.5 только там, где этого требуют сертификаты или третьи стороны.
  5. Если вы всё равно проводите ротацию ключей, рассмотрите вместо RSA ECDSA P-256 или Ed25519: они компактнее и подписывают быстрее, чем RSA с любым дополнением.

Источники

Все сравнения