DeutschGitHub
prototypepackages/signature/src

Signature

@ralphschuler/signature

Signs JSON envelopes with RSA-SHA256 and encrypts key exports using AES-GCM.

WebCryptoRSA-PSSkey rotation

01 · Problem

What is it for?

Integrity verification and private-key confidentiality are different operations with different algorithms.

02 · Mental model

How should you think about it?

Sign a defined byte representation with a private key; verify it with the matching public key.

In this repository

Signs JSON envelopes with RSA-SHA256 and encrypts key exports using AES-GCM. Keys are local to the signer. Production trust distribution, canonical serialization and rotation policy need an application design.

03 · Control flow

What happens in which order?

  1. Generate an RSA key pair.
  2. Construct a signature header.
  3. Serialize header and payload together.
  4. Sign with the private key.
  5. Reconstruct the same bytes and verify.

04 · Building blocks

The important contracts

Signature.signData / verifySignatureCreate and verify a JSON signature envelope.
KeyPairEncryptionEncrypt exported private keys using scrypt and AES-256-GCM.
PayloadSignerSign and verify exact serialized bytes.

05 · Build it yourself

Implement a small core

Start with this small independent implementation, then use the steps below to make its contract explicit.

  1. Generate an RSA key pair.
  2. Construct a signature header.
  3. Serialize header and payload together.
  4. Sign with the private key.
  5. Reconstruct the same bytes and verify.
Concept sketch · may reference omitted helpers
async function sign(payload: unknown, privateKey: CryptoKey) {
  const bytes = new TextEncoder().encode(canonicalJson(payload));
  const signature = await crypto.subtle.sign(
    { name: "RSA-PSS", saltLength: 32 },
    privateKey,
    bytes,
  );
  return new Uint8Array(signature);
}

async function verify(payload: unknown, value: BufferSource, publicKey: CryptoKey) {
  const bytes = new TextEncoder().encode(canonicalJson(payload));
  return crypto.subtle.verify({ name: "RSA-PSS", saltLength: 32 }, publicKey, value, bytes);
}

06 · Verify

What should you test?

  • Verify a valid signature and reject a modified payload.
  • Test unknown key IDs and altered headers.
  • Round-trip encrypted keys and reject wrong passwords or authentication tags.

Try it yourself

Turn the first verification claim into a failing test, implement the smallest fix, then add a boundary case.

Verify a valid signature and reject a modified payload.

Compare your approach

Your check should fail when the stated invariant is deliberately broken, and pass again after the fix. Keep setup deterministic and assert an observable result.

07 · Limits

Trade-offs and failure modes

  • JSON serialization order is part of this envelope contract.
  • RSA-OAEP is encryption, not a signature algorithm.
  • Production key rotation and trust distribution require an explicit policy.
Important

Keys are local to the signer. Production trust distribution, canonical serialization and rotation policy need an application design.

08 · Keep exploring

Source code and related ideas

yarn tsx packages/signature/examples/basic.ts