GitHub
Prototyppackages/signature/src

Signature

@ralphschuler/signature

Signiert versionierte Payload-Bytes und verwaltet dafür Key-IDs, Rotation und passwortgeschützte private Schlüssel.

WebCryptoRSA-PSSkey rotation

01 · Problem

Wofür braucht man das?

Empfänger müssen Integrität und Herkunft einer Nachricht prüfen können, ohne Zugriff auf den privaten Schlüssel zu besitzen.

02 · Denkmodell

Das mentale Modell

Header plus Payload werden kanonisch serialisiert und mit Private Key signiert; Key-ID wählt beim Prüfen den passenden Public Key.

Im Repository

Signature, PayloadSigner und KeyPairEncryption werden exportiert, doch CryptoKey/KeyObject und Persistenztypen widersprechen sich. Der Konstruktor startet nicht awaitbare Keygenerierung und AES-CBC-Daten lassen sich nicht korrekt JSON-serialisieren.

03 · Kontrollfluss

Was passiert in welcher Reihenfolge?

  1. Keypair über eine explizite async Factory erzeugen oder laden.
  2. Versionierten Header und Payload kanonisch in Bytes wandeln.
  3. Bytes mit dem privaten Signaturschlüssel signieren.
  4. Key-ID auflösen, Timestamp-Policy prüfen und dieselben Bytes verifizieren.
  5. Private Keys versioniert und authentifiziert verschlüsselt persistieren.

04 · Bauteile

Die entscheidenden Verträge

Signature<T> High-Level-Fassade mit drei rotierenden RSA-Keypairs.
PayloadSigner<T> Erstellt Header und signiert/verifiziert serialisierte Daten.
KeyPairEncryption Exportiert Keys und verschlüsselt Private-Key-Material per PBKDF2/AES-CBC.
KeyPairManager (intern) Erzeugt, rotiert und findet Keys, wird aber nicht am Root exportiert.

05 · Build it yourself

Selbst implementieren

Beginne mit einer awaitbaren Factory und einem festen Signaturalgorithmus; Persistenz ist ein separater Schritt.

  1. Keypair vollständig erzeugen, bevor eine Signer-Instanz zurückgegeben wird.
  2. Header und Payload in ein kanonisches, versioniertes Byteformat bringen.
  3. Private-Key-Export mit eigener KDF-Salt und AES-GCM-IV als Base64url speichern.
minimal.ts · unabhängig vom Package
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 · Verifizieren

Was du testen solltest

  • Originalpayload verifiziert; veränderter Header, Payload oder Signatur verifiziert nicht.
  • Signieren ist vor abgeschlossener Keyinitialisierung durch die API unmöglich.
  • Export/Encrypt/Serialize/Parse/Decrypt/Import bildet den privaten Key verlustfrei ab und erkennt Manipulation.

07 · Grenzen

Kompromisse und Stolperfallen

  • RSA-PSS ist breit verfügbar, aber Key und Signatur sind groß.
  • Kanonische Serialisierung ist zusätzlicher Aufwand und unverzichtbar für reproduzierbare Bytes.
  • Keyrotation begrenzt Schäden, erfordert aber Historie, Ablauf und Revocation.
Wichtig

RSA-OAEP ist ein Verschlüsselungs-, kein Signaturalgorithmus. Für persistierte Secrets authentifizierte Verschlüsselung wie AES-GCM verwenden.

08 · Weiterdenken

Quellcode und Nachbarn

Originalcode auf GitHub ansehen