RS256: RSA-SHA256 JWTs

RS256 signs a JWT with an RSA private key (PKCS#1 v1.5 padding, SHA-256). Anyone with the public key can verify it, but only the private-key holder can create tokens — the most widely supported asymmetric JWT algorithm.

How RS256 works

signature = RSASSA-PKCS1-v1_5-SIGN(privateKey, SHA-256(b64url(header) + "." + b64url(payload)))
verify    = RSASSA-PKCS1-v1_5-VERIFY(publicKey, …)

The signature is as long as the RSA modulus: 256 bytes for a 2048-bit key, which is why RS256 tokens are noticeably longer than HS256 or ES256 tokens.

Generating and formatting keys

Use at least 2048 bits (RFC 7518 §3.3); 3072 bits is a common choice for keys that live for years. You can generate a pair in the browser with the key generator, or with OpenSSL:

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out private.pem
openssl pkey -in private.pem -pubout -out public.pem

JWTEncoder accepts PKCS#8 (BEGIN PRIVATE KEY), PKCS#1 (BEGIN RSA PRIVATE KEY, converted locally) and JWK private keys. Paste a public key and you get a specific explanation: signing requires the private key.

Publishing the public key (JWKS)

Verifiers usually fetch public keys from a JWK Set. Give every key a kid (the generator uses the RFC 7638 thumbprint), put that kid in the token header, and serve the JWKS at a stable HTTPS URL. During rotation, publish both old and new keys until old tokens expire.

Sign RS256 in code

import { SignJWT, importPKCS8 } from "jose";

const key = await importPKCS8(process.env.JWT_PRIVATE_KEY_PEM, "RS256");
const now = Math.floor(Date.now() / 1000);

const payload = {
  iss: "https://auth.example.com/",
  sub: "user-123",
  aud: "api.example.com",
  iat: now,
  exp: now + 900,
};

const token = await new SignJWT(payload)
  .setProtectedHeader({
    alg: "RS256",
    typ: "JWT",
    kid: "2026-09",
  })
  .sign(key);

console.log(token);

The same RSA key pair works for PS256 (RSA-PSS). Choose PS256 when every verifier supports it.

Generate a key pair or paste your own private key, build claims and sign — with self-verification before you copy.

Create an RS256 JWT