RSA-PSS vs PKCS#1 v1.5, c’est le choix de la façon dont un condensat est encodé avant l’opération RSA à clé privée. Les deux viennent de PKCS #1 (RFC 8017), les deux sont approuvés par FIPS 186-5 et les deux fonctionnent avec la même paire de clés RSA. La différence tient à leurs arguments de sécurité, à leur déterminisme et aux paramètres qui doivent concorder entre le signataire et le vérificateur.
Vue d’ensemble
| RSASSA-PSS | RSASSA-PKCS1-v1_5 | |
|---|---|---|
| Spécification | RFC 8017 §8.1, encodage EMSA-PSS §9.1 | RFC 8017 §8.2, encodage EMSA-PKCS1-v1_5 §9.2 |
| Encodage | Condensat salé, masqué avec MGF1 | 0x00 01 FF…FF 00 ‖ DigestInfo |
| Aléatoire | Oui (sauf si la longueur du sel vaut 0) | Non : même clé et même message donnent la même signature |
| Preuve de sécurité | Réduction fine au problème RSA dans le modèle de l’oracle aléatoire | Aucune preuve comparable ; aucune attaque pratique contre les vérificateurs corrects |
| Paramètres à faire concorder | Fonction de hachage, fonction de hachage de la MGF, longueur du sel, octet final (trailer) | Fonction de hachage uniquement |
| Taille de la signature | Taille du module (256 octets pour RSA-2048) | Identique |
| Vitesse | Même opération RSA | Identique |
| JOSE | PS256, PS384, PS512 (sel = longueur du condensat) | RS256, RS384, RS512 |
| TLS 1.3 | Obligatoire pour CertificateVerify avec RSA (rsa_pss_rsae_sha256 0x0804, rsa_pss_pss_sha256 0x0809) | Uniquement pour les signatures à l’intérieur des certificats (rsa_pkcs1_sha256 0x0401) |
| X.509 | id-RSASSA-PSS 1.2.840.113549.1.1.10 avec paramètres | sha256WithRSAEncryption 1.2.840.113549.1.1.11 |
| Contrainte de FIPS 186-5 | Longueur du sel comprise entre 0 et la longueur du condensat | Contrôles du DigestInfo au §5.4(h) |
Différences de sécurité
Le remplissage de signature PKCS#1 v1.5 date de 1991. Il est déterministe et n’a jamais eu de preuve de sécurité aussi nette que celle de PSS, mais après trois décennies, aucune falsification pratique n’existe contre les vérificateurs qui contrôlent l’intégralité du bloc encodé. Les failles réelles étaient des bugs d’implémentation : l’attaque de Bleichenbacher de 2006 falsifiait des signatures pour l’exposant public 3 auprès d’analyseurs qui ignoraient les octets superflus après le DigestInfo, et des variantes de cette erreur ont refait surface dans des bibliothèques comme NSS (BERserk, 2014), entre autres. Le §8.2.2 de la RFC 8017 écarte toute cette catégorie en réencodant le bloc attendu et en comparant les octets, et le §5.4(h) de FIPS 186-5 codifie les contrôles.
RSASSA-PSS (Bellare et Rogaway, normalisé dans PKCS #1 v2.1 en 2002) hache un sel aléatoire avec le condensat du message et masque le résultat avec MGF1. Sa sécurité se réduit finement au problème RSA dans le modèle de l’oracle aléatoire, et son caractère aléatoire fait qu’une faute ou un canal auxiliaire sur une signature en révèle moins. Avec un sel de longueur nulle, PSS devient déterministe mais conserve sa preuve, sous des hypothèses légèrement différentes.
Ne confondez ni l’un ni l’autre avec le remplissage de chiffrement PKCS #1 v1.5, réellement cassé par les attaques par oracle de remplissage (Bleichenbacher 1998, ROBOT 2017) et retiré de TLS 1.3. Le remplissage de signature et le remplissage de chiffrement sont des schémas distincts qui partagent un nom.
Interopérabilité : le piège de la longueur du sel
PSS exige que le signataire et le vérificateur s’accordent sur trois paramètres : la fonction de hachage, la fonction de hachage de MGF1 et la longueur du sel. JOSE fixe les trois (PS256 signifie SHA-256, MGF1-SHA-256 et un sel de 32 octets), et TLS 1.3 exige que la longueur du sel soit égale à celle du condensat. Ailleurs, il faut vérifier : OpenSSL 1.1.1 et 3.0 signaient par défaut avec le sel maximal (222 octets pour RSA-2048 avec SHA-256), alors qu’OpenSSL 3.1 et les versions ultérieures utilisent par défaut auto-digestmax, qui produit un sel de la taille du condensat. Les vérificateurs configurés pour une longueur de sel fixe rejettent alors les signatures de l’autre génération.
Le script ci-dessous, exécuté avec OpenSSL 3.6.5, montre les deux comportements.
#!/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
Les vérificateurs qui acceptent auto déduisent la longueur du sel de la signature : c’est le réglage le plus compatible quand vous ne maîtrisez pas le signataire.
Clés et certificats
La même clé RSA peut produire les deux types de signature, et une clé rsaEncryption dans un certificat sert aux deux (TLS appelle ce cas rsa_pss_rsae). X.509 définit aussi des clés id-RSASSA-PSS dont les paramètres les limitent à PSS (rsa_pss_pss en TLS) ; elles empêchent l’usage d’une même clé avec les deux schémas, mais de nombreux outils et certains navigateurs les gèrent mal : les clés rsaEncryption qui signent en PSS sont donc le choix pratique. Dans la WebPKI, les signatures de certificats sont presque toutes en PKCS#1 v1.5 ou en ECDSA, et les vérificateurs doivent prendre en charge v1.5 pour cette seule raison.
Quand choisir l’un ou l’autre
- Handshakes TLS 1.3 : PSS, obligatoire ; votre bibliothèque s’en charge automatiquement avec tout certificat RSA.
- JWT :
PS256quand tous les consommateurs le prennent en charge,RS256sinon ; mieux encore, envisagezES256(voir ES256 vs RS256). - Certificats X.509 et CRL que vous émettez : PKCS#1 v1.5 (
sha256WithRSAEncryption) pour une compatibilité maximale, sauf si un écosystème fermé a adopté PSS comme norme. - Nouvelles signatures de documents, de micrologiciels ou d’API avec des clés RSA : PSS avec une longueur de sel égale à la longueur du condensat.
- Interopérabilité avec les HSM et les KMS cloud : les deux sont pris en charge (par exemple
RSASSA_PSS_SHA_256etRSASSA_PKCS1_V1_5_SHA_256dans AWS KMS) ; alignez-vous sur ce qu’attend le vérificateur. - Vérification de l’un ou l’autre : utilisez la fonction de vérification de haut niveau d’une bibliothèque, jamais un décodage manuel du message encodé.
Notes de migration
Passer de v1.5 à PSS ne nécessite pas de nouvelles clés, ce qui permet de procéder facilement par étapes.
- Activez d’abord PSS côté vérificateurs : en JOSE, autorisez
PS256pour la clé (la règle reste un algorithme par clé : publiez donc le même matériel de clé sous un nouveaukidavecalgPS256, ou effectuez une rotation). - Fixez explicitement les paramètres : fonction de hachage SHA-256, MGF1 SHA-256, longueur de sel de 32 octets ; évitez les valeurs par défaut des bibliothèques, qui varient d’une version à l’autre.
- Basculez les signataires et surveillez les échecs de vérification qui mentionnent la longueur du sel ou le remplissage.
- Cessez d’accepter v1.5 pour ces clés une fois les anciennes signatures expirées, en ne gardant v1.5 que là où des certificats ou des tiers l’exigent.
- Si vous procédez de toute façon à une rotation des clés, envisagez plutôt ECDSA P-256 ou Ed25519, plus compacts et plus rapides en signature que n’importe quel remplissage RSA.