RSASSA-PSS 是 RSA 签名的推荐填充方式,但 Java 里它的参数、盐长度与密钥类型处处是坑。这篇在 JDK 21 上逐项量出:API 的要求、盐的作用与上限、验证时参数不一致的后果、PSS 类型密钥的限制、与 OpenSSL 的互通,以及性能。
RSASSA-PSS 不 setParameter 直接 initSign / sign:SignatureException:Parameters required for RSASSA-PSS signatures
Signature.getInstance("SHA256withRSA/PSS"):NoSuchAlgorithmException:SHA256withRSA/PSS Signature not available
Signature.getInstance("SHA256withRSAandMGF1"):NoSuchAlgorithmException:SHA256withRSAandMGF1 Signature not available
Signature.getInstance("RSASSA-PSS"):SignatureException:Parameters required for RSASSA-PSS signatures
Signature.getInstance("SHA1withRSA/PSS"):NoSuchAlgorithmException:SHA1withRSA/PSS Signature not available
签名参数:getParameters() = RSASSA-PSS SHA-256/32
RSA 2048 + SHA-256 + 盐 32:签名 256 字节(与密钥的模长相同)盐长度 0:同一消息签 20 次,不同的签名个数 = 1 盐长度 20:同一消息签 20 次,不同的签名个数 = 20 盐长度 32:同一消息签 20 次,不同的签名个数 = 20 RSA 2048 + SHA-256 + 盐 222:签名成功 RSA 2048 + SHA-256 + 盐 223:InvalidKeyException:Key is too short, need min 257 bytes RSA 2048 + SHA-512 + 盐 190:签名成功 RSA 2048 + SHA-512 + 盐 191:InvalidKeyException:Key is too short, need min 257 bytes RSA 2048 + SHA-1 + 盐 234:签名成功 RSA 2048 + SHA-1 + 盐 235:InvalidKeyException:Key is too short, need min 257 bytes 上限的计算:emBits = 2048 - 1 = 2047,emLen = ceil(2047 / 8) = 256 字节,最大盐 = emLen - hLen - 2(SHA-256 为 256 - 32 - 2 = 222;SHA-512 为 256 - 64 - 2 = 190;SHA-1 为 256 - 20 - 2 = 234)
同样的参数验证:true 验证时盐长度写 20(签名用 32):false 验证时摘要写 SHA-384(签名用 SHA-256):false 验证时 MGF1 用 SHA-1、摘要用 SHA-256:false 验证时用 PKCS#1 v1.5(SHA256withRSA):false 签名翻转 1 位:false 消息改一个字节:false PKCS#1 v1.5 的签名用 PSS 验证:false 盐 0 签名,验证时写盐 32:false;写盐 0:true
RSASSA-PSS 类型的密钥:算法 RSASSA-PSS,公钥编码 346 字节(普通 RSA 公钥是 294 字节,多了 PSS 参数),参数 PSSParameterSpec SHA-256/盐 32 用与密钥参数一致的 PSS 参数签名:成功;验证 true 摘要 SHA-512(密钥限定 SHA-256)签名:InvalidKeyException:Key contains incompatible PSS parameter values 盐 16(密钥限定最小盐 32)签名:InvalidKeyException:Key contains incompatible PSS parameter values 盐 40(密钥限定最小盐 32)签名:成功 PSS 密钥不 setParameter 签名:SignatureException:Parameters required for RSASSA-PSS signatures 把 PSS 密钥用于 SHA256withRSA(PKCS#1 v1.5):InvalidKeyException:Invalid key for RSA signatures 普通 RSA 私钥配 PSS 参数:成功
openssl 验证 Java 盐 32 的签名(指定盐 32):Signature Verified Successfully openssl 验证 Java 盐 0 的签名(指定盐 0):Signature Verified Successfully openssl 验证 Java 盐 32 的签名(指定盐 0):Signature Verification Failure openssl 验证 Java 盐 32 的签名(rsa_pss_saltlen:auto):Signature Verified Successfully openssl 用 PSS 签名(盐 32):退出码 0,256 字节 openssl 用 PSS 签名(盐 默认(不指定)):退出码 0,256 字节 openssl 用 PSS 签名(盐 max):退出码 0,256 字节 Java 验证 ossl_salt32.sig(OpenSSL 生成)盐 32:true;盐 222(验证时写成最大盐):false;写盐 20:false Java 验证 ossl_default.sig(OpenSSL 生成)盐 32:true;盐 222(验证时写成最大盐):false;写盐 20:false Java 验证 ossl_max.sig(OpenSSL 生成)盐 32:false;盐 222(验证时写成最大盐):true;写盐 20:false
RSA 2048 PKCS#1 v1.5(SHA256withRSA) 签名 872 µs/次,验证 37.5 µs/次 RSA 2048 PSS(SHA-256,盐 32) 签名 872 µs/次,验证 36.3 µs/次 RSA 3072 PKCS#1 v1.5 签名 2539 µs/次,验证 68.4 µs/次 RSA 3072 PSS(SHA-256,盐 32) 签名 2544 µs/次,验证 68.4 µs/次