Files
2026-05-27 23:47:53 +01:00

2.8 KiB

RSASSA-PKCS1-v1_5 algorithms (RS256 / RS384 / RS512)

Purpose

RFC 7518 §3.3 RSASSA-PKCS1-v1_5 signing and verification. The signing input is hashed with SHA-256/SHA-384/SHA-512 respectively, EMSA-PKCS1-v1_5-encoded to the modulus length, and signed via the modular-exponentiation primitive from @suaveplan/crypto-rsa. This is the dominant JWS algorithm in production (OAuth/OIDC JWT signing, SafetyNet, Apple App Attest, GitHub workflow tokens) and the algorithm webauthn is unblocked on.

Features

  • RS256 → RSASSA-PKCS1-v1_5 + SHA-256.
  • RS384 → RSASSA-PKCS1-v1_5 + SHA-384.
  • RS512 → RSASSA-PKCS1-v1_5 + SHA-512.
  • Delegates to @suaveplan/crypto-rsa's rsaVerifyPkcs1v15 / rsaSignPkcs1v15.
  • Key shape: RsaPublicKey (verify) / RsaPrivateKey (sign) — both from @suaveplan/crypto-rsa.

Usage: Basic Example

import { generateRsaKeyPair, parseRsaPublicKeyJwk } from "@suaveplan/crypto-rsa";
import { compactSign, compactVerify } from "@suaveplan/jws";

const { publicKey, privateKey } = await generateRsaKeyPair({ modulusLength: 2048 });
const token = await compactSign("hello", privateKey, "RS256");
const r = await compactVerify(token, publicKey, { algorithms: ["RS256"] });

Usage: Advanced Example

import { parseRsaPublicKey } from "@suaveplan/crypto-rsa";
import { compactVerify, rs256 } from "@suaveplan/jws";

// Verify a JWS whose key arrived as a DER-encoded SPKI from a registry:
const spkiDer = await fetch("/.well-known/jwks-spki.der").then((r) => r.bytes());
const pub = parseRsaPublicKey(spkiDer, { format: "spki" });
const result = await compactVerify(token, pub, {
    algorithms: ["RS256", "RS384"],
});

// Direct-impl access (e.g. signing custom binary payloads):
const sig = await rs256.sign!(signingInput, priv);

API Reference

  • rs256: AlgorithmImpl<RsaPublicKey | RsaPrivateKey>
  • rs384: AlgorithmImpl<RsaPublicKey | RsaPrivateKey>
  • rs512: AlgorithmImpl<RsaPublicKey | RsaPrivateKey>

Each verify returns Promise<boolean>; each sign returns Promise<Uint8Array> of length Math.ceil(bitLength/8). Non-RSA-shaped keys throw JwsError(JWS.KEY_ALG_MISMATCH).

Implementation Notes

This module is a thin façade over @suaveplan/crypto-rsa — it never reimplements PKCS#1 v2.2 or modular arithmetic. Verification swallows any low-level CryptoRsaError and returns false so structural malformation surfaces as JWS.SIG_INVALID at the pipeline level rather than leaking RSA-package-internal codes; signing propagates because a sign-side error means the caller's key is structurally wrong (modulus too short, missing CRT parameters, etc.) and they need the precise diagnostic. RSA private operations in @suaveplan/crypto-rsa run through CRT and apply RSA blinding (Kocher 1996 timing-attack defence) by default.