SovereignRoot blog

Signing JSON in the browser with Web Crypto

A practical, dependency-free guide to signing a JSON document entirely in the browser — so the private key never leaves the device.

Why sign in the browser?

For a local-first tool, the private key should never touch a server. Web Crypto lets you generate keys, sign documents and verify signatures entirely in the browser — no backend, no key upload, no third-party dependency.

The pieces you need

  • Key generation: ECDSA P-256 via crypto.subtle.generateKey.
  • Canonicalization: RFC 8785 (JCS) so the exact bytes you hash match what a verifier hashes.
  • Digest: SHA-256 of the canonical UTF-8 payload.
  • Signature: ECDSA-SHA256, base64url encoded.
  • Identity: RFC 7638 JWK thumbprint → RFC 9278 thumbprint URI.
  • Key export: encrypt the private JWK with PBKDF2 + AES-256-GCM before download.

The canonicalization trap

JSON object key order is not guaranteed. If you hash the raw JSON, a verifier that re-serializes differently gets a different digest. RFC 8785 canonicalization fixes this — it produces a deterministic byte representation so both sides hash the same thing.

Never sign secrets

A signed document is portable and tamper-evident, but it's not a secret store. Keep passwords, API keys, tokens and private keys out of the signed payload.

See it working

SovereignRoot is a working implementation of exactly this — a local-first generator and verifier built on Web Crypto. Try it, or read the reference code.

Try the generator