Saltar al contenido
ecdsa.com

RSA-PSS o PKCS#1 v1.5: qué relleno de firma RSA usar

Mismas claves RSA, mismos tamaños, misma velocidad. PSS tiene una demostración de seguridad y es obligatorio en los handshakes de TLS 1.3; PKCS#1 v1.5 está en todas partes y, si se verifica correctamente, no tiene ningún ataque práctico.

RSA-PSS frente a PKCS#1 v1.5 es una elección sobre cómo se codifica un hash antes de la operación RSA con la clave privada. Ambos proceden de PKCS #1 (RFC 8017), ambos están aprobados en FIPS 186-5 y ambos funcionan con el mismo par de claves RSA. La diferencia está en sus argumentos de seguridad, en su determinismo y en los parámetros que deben coincidir entre el firmante y el verificador.

De un vistazo

RSASSA-PSSRSASSA-PKCS1-v1_5
EspecificaciónRFC 8017 §8.1, codificación EMSA-PSS §9.1RFC 8017 §8.2, codificación EMSA-PKCS1-v1_5 §9.2
CodificaciónHash con sal, enmascarado con MGF10x00 01 FF…FF 00 ‖ DigestInfo
AleatorizadoSí (salvo que la longitud de la sal sea 0)No: la misma clave y el mismo mensaje dan la misma firma
Demostración de seguridadReducción ajustada a RSA en el modelo del oráculo aleatorioNinguna demostración comparable; ningún ataque práctico contra verificadores correctos
Parámetros que hay que acordarHash, hash de MGF, longitud de la sal, campo final (trailer)Solo el hash
Tamaño de firmaTamaño del módulo (256 bytes para RSA-2048)Igual
VelocidadLa misma operación RSAIgual
JOSEPS256, PS384, PS512 (sal = longitud del hash)RS256, RS384, RS512
TLS 1.3Obligatorio para CertificateVerify con RSA (rsa_pss_rsae_sha256 0x0804, rsa_pss_pss_sha256 0x0809)Solo para las firmas dentro de los certificados (rsa_pkcs1_sha256 0x0401)
X.509id-RSASSA-PSS 1.2.840.113549.1.1.10 con parámetrossha256WithRSAEncryption 1.2.840.113549.1.1.11
Restricción de FIPS 186-5Longitud de la sal entre 0 y la longitud del hashComprobaciones de DigestInfo en §5.4(h)

Diferencias de seguridad

El relleno de firma de PKCS#1 v1.5 data de 1991. Es determinista y nunca ha tenido una demostración de seguridad tan limpia como la de PSS, pero tras tres décadas no existe ninguna falsificación práctica contra los verificadores que comprueban el bloque codificado completo. Los fallos reales fueron errores de implementación: el ataque de Bleichenbacher de 2006 falsificaba firmas con exponente público 3 frente a analizadores que ignoraban los bytes sobrantes después de DigestInfo, y variantes de ese error reaparecieron en bibliotecas como NSS (BERserk, 2014) y otras. El RFC 8017 §8.2.2 evita toda esa clase de fallos volviendo a codificar el bloque esperado y comparando los bytes, y FIPS 186-5 §5.4(h) fija esas comprobaciones como norma.

RSASSA-PSS (Bellare y Rogaway, estandarizado en PKCS #1 v2.1, 2002) calcula el hash de una sal aleatoria junto con el hash del mensaje y enmascara el resultado con MGF1. Su seguridad se reduce de forma ajustada al problema RSA en el modelo del oráculo aleatorio, y su aleatorización hace que un fallo inducido o un canal lateral en una firma revele menos. Con una sal de longitud cero, PSS pasa a ser determinista, pero conserva su demostración bajo supuestos ligeramente distintos.

No confundas ninguno de los dos con el relleno de cifrado de PKCS #1 v1.5, que sí está roto por los ataques de oráculo de relleno (Bleichenbacher 1998, ROBOT 2017) y se eliminó de TLS 1.3. El relleno de firma y el de cifrado son esquemas distintos que comparten nombre.

Interoperabilidad: la trampa de la longitud de la sal

PSS exige que el firmante y el verificador acuerden tres parámetros: el hash, el hash de MGF1 y la longitud de la sal. JOSE fija los tres (PS256 significa SHA-256, MGF1-SHA-256 y sal de 32 bytes), y TLS 1.3 exige que la longitud de la sal sea igual a la del hash. En los demás casos tienes que comprobarlo: OpenSSL 1.1.1 y 3.0 firmaban por defecto con la sal máxima (222 bytes para RSA-2048 con SHA-256), mientras que OpenSSL 3.1 y posteriores usan por defecto auto-digestmax, que produce una sal del tamaño del resumen. Así, los verificadores configurados con una longitud de sal fija rechazan las firmas de la otra generación.

El script siguiente, ejecutado con OpenSSL 3.6.5, muestra ambos comportamientos.

OpenSSL: determinismo y comprobación de la longitud de la sal en 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"
Salida
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

Los verificadores que aceptan auto detectan la longitud de la sal a partir de la firma, que es la opción más compatible cuando no controlas el firmante.

Claves y certificados

La misma clave RSA puede producir los dos tipos de firma, y una clave rsaEncryption de un certificado sirve para ambos (TLS lo llama rsa_pss_rsae). X.509 también define claves id-RSASSA-PSS cuyos parámetros las restringen a PSS (rsa_pss_pss en TLS); impiden el uso cruzado entre esquemas, pero muchas herramientas y algunos navegadores las gestionan mal, así que las claves rsaEncryption que firman con PSS son la opción práctica. Las firmas de certificados de la WebPKI son casi todas PKCS#1 v1.5 o ECDSA, y solo por eso los verificadores tienen que admitir v1.5.

Cuándo elegir cada uno

  • Handshakes de TLS 1.3: PSS, obligatorio; tu biblioteca lo hace automáticamente con cualquier certificado RSA.
  • JWT: PS256 cuando todos los consumidores lo admitan y RS256 en caso contrario; mejor aún, plantéate ES256 (consulta ES256 frente a RS256).
  • Certificados X.509 y CRL que emites: PKCS#1 v1.5 (sha256WithRSAEncryption) para la máxima compatibilidad, salvo que un ecosistema cerrado haya adoptado PSS como estándar.
  • Firmas nuevas de documentos, firmware o API con claves RSA: PSS con una longitud de sal igual a la del hash.
  • Interoperabilidad con HSM y KMS en la nube: se admiten los dos esquemas (por ejemplo, RSASSA_PSS_SHA_256 y RSASSA_PKCS1_V1_5_SHA_256 en AWS KMS); ajústate a lo que espera el verificador.
  • Verificación de cualquiera de los dos: usa la función de verificación de alto nivel de una biblioteca, nunca una decodificación manual del mensaje codificado.

Notas de migración

Pasar de v1.5 a PSS no requiere claves nuevas, lo que facilita hacerlo por etapas.

  1. Primero, haz que los verificadores acepten PSS: en JOSE, permite PS256 para la clave (la regla sigue siendo un algoritmo por clave, así que publica el mismo material de clave con un kid nuevo y alg PS256, o rota la clave).
  2. Fija los parámetros de forma explícita: hash SHA-256, MGF1 SHA-256, longitud de sal de 32 bytes; evita los valores por defecto de las bibliotecas, que cambian de una versión a otra.
  3. Cambia los firmantes y vigila los fallos de verificación que mencionen la longitud de la sal o el relleno.
  4. Deja de aceptar v1.5 para esas claves cuando hayan caducado las firmas antiguas, y mantén v1.5 solo donde lo exijan los certificados o terceros.
  5. Si de todos modos vas a rotar claves, valora en su lugar ECDSA P-256 o Ed25519, que son más pequeños y firman más rápido que RSA con cualquier relleno.

Referencias

Todas las comparaciones