PS256 JWT — RSA-PSS SHA-256 vs RS256
PS256 (RSA-PSS SHA-256) is RFC 7518's upgrade from RS256: same RSA keys, one algorithm change. Sign/verify code and migration from RS256.
PS256 is RSASSA-PSS with SHA-256. It uses the same RSA key pairs as RS256 but with PSS (Probabilistic Signature Scheme) padding instead of the older PKCS#1 v1.5 padding. PS256 is a drop-in cryptographic upgrade from RS256: the same private and public keys, the same JWKS, just a different algorithm identifier. RFC 7518 recommends PS256 over RS256 for new deployments. Defined in RFC 7518 §3.5.
Key facts and signature size
| Property | PS256 |
|---|---|
| Type | Asymmetric (RSA, PSS padding) |
| Key | 2048-bit RSA minimum (same keys as RS256) |
| Signature size | Key size ÷ 8 (same as RS256) |
| Security | ~112-bit for 2048-bit keys; proven reduction to RSA problem |
| Compatibility | Modern: jsonwebtoken ≥9.x, PyJWT ≥2.x, jjwt, jose |
When to use
Use PS256 when you already use RS256 and want a free cryptographic upgrade: the same RSA key pair, one-line algorithm change in your library, immediate security improvement. Use PS256 for regulated financial APIs: the FAPI 1.0 Advanced profile mandates PS256 or ES256 and explicitly discourages RS256. Open Banking (UK) and several financial API standards require PS256.
When not to use
PS256 is not supported by older JWT libraries or some FIPS-restricted/embedded environments. Verify support across your full stack before switching. If you need smaller tokens than RS256/PS256, use ES256 or EdDSA instead: PS256 has the same 256-byte signature as RS256.
Code examples
Migrate RS256 → PS256 (one-line change, same RSA keys)
// Before (RS256)
const token = jwt.sign(payload, privateKey, { algorithm: "RS256" });
const decoded = jwt.verify(token, publicKey, { algorithms: ["RS256"] });
// After (PS256): same RSA keys, only the algorithm string changes
const token = jwt.sign(payload, privateKey, { algorithm: "PS256" });
const decoded = jwt.verify(token, publicKey, { algorithms: ["PS256"] }); Sign and verify (Python: PyJWT)
token = jwt.encode(
{"sub": "user_123"},
private_key, # same RSA private key as RS256
algorithm="PS256",
headers={"kid": "rsa-key-v1"},
)
payload = jwt.decode(
token,
public_key, # same RSA public key as RS256
algorithms=["PS256"],
issuer="https://auth.example.com",
audience="https://api.example.com",
) Frequently asked questions
-
What is PS256 and why use it over RS256?
PS256 is RSA with PSS padding instead of the older PKCS#1 v1.5 padding used by RS256. It uses the same RSA key pair, so migrating from RS256 to PS256 is a one-line algorithm change in your JWT library with no key rotation. RFC 7518 recommends PS256 over RS256 for new deployments because PSS has a formal security proof (a reduction to the RSA problem) that PKCS#1 v1.5 lacks, and PSS is not susceptible to Bleichenbacher padding-oracle attacks.
-
Can I switch from RS256 to PS256 without rotating keys?
Yes. RS256 and PS256 use identical RSA key pairs: only the padding scheme inside the RSA operation differs. To migrate, change the algorithm string in your signing call (jwt.sign(payload, key, { algorithm: 'PS256' })) and your verification allowlist (jwt.verify(token, key, { algorithms: ['PS256'] })). Token size is identical. The only caveat is library support: verify every verifier in your stack supports PS256 before switching.
-
Does FAPI / Open Banking require PS256?
The FAPI 1.0 Advanced security profile mandates PS256 or ES256 and explicitly discourages RS256. Open Banking in the UK and several regulated financial API standards require PS256. If you are building a financial-grade or regulated API, use PS256 (or ES256): not RS256: to comply with the profile.