Skip to content

JWT Signing (RS256)

Create a JSON Web Token signed with RSA-SHA256 on wasm runtimes such as Cloudflare Workers, Deno, or the browser. This is the standard authentication flow for GitHub Apps, Google service accounts, and many OAuth 2.0 providers.

sh
cargo add wasm_web_crypto --features sign,key

Import a private key and sign

Import a PKCS#8 DER private key, sign the header.payload string, and append the base64url-encoded signature.

rust
use wasm_web_crypto::{Algorithm, Hash, KeyFormat, KeyUsage, SubtleCrypto};

let subtle = SubtleCrypto::new()?;

// Import a PKCS#8 DER private key
let key = subtle.import_key(
    KeyFormat::Pkcs8,
    &private_key_der,
    &Algorithm::RsassaPkcs1v15 { hash: Hash::Sha256 },
    false,
    &[KeyUsage::Sign],
).await?;

// Sign the JWT header.payload
let signature = subtle.sign(
    &Algorithm::RsassaPkcs1v15 { hash: Hash::Sha256 },
    &key,
    signing_input.as_bytes(),
).await?;

let jwt = format!("{}.{}", signing_input, signature.to_base64url());

Verify with a public key

rust
let public_key = subtle.import_key(
    KeyFormat::Spki,
    &public_key_der,
    &Algorithm::RsassaPkcs1v15 { hash: Hash::Sha256 },
    false,
    &[KeyUsage::Verify],
).await?;

let valid = subtle.verify(
    &Algorithm::RsassaPkcs1v15 { hash: Hash::Sha256 },
    &public_key,
    signature.to_bytes(),
    signing_input.as_bytes(),
).await?;

assert!(valid);

PEM to DER conversion

Web Crypto API accepts DER binary, not PEM text. To convert:

  1. Remove -----BEGIN PRIVATE KEY----- / -----END PRIVATE KEY----- lines
  2. Base64-decode the remaining content
  3. Pass the resulting bytes to import_key