Start · Sprachen · JavaScript · Referenz · SubtleCrypto.sign()

SubtleCrypto.sign()

Methode

Erzeugt mit einem privaten oder geheimen <code>CryptoKey</code> eine digitale Signatur bzw. einen MAC über beliebige Binärdaten und gibt sie als <code>ArrayBuffer</code> zurück.

seit JavaScript Baseline: alle modernen Browser (Ed25519 seit 2025 Kategorie: webapi

Signatur

SubtleCrypto.prototype.sign(algorithm, key, data)

Beschreibung

crypto.subtle.sign() berechnet eine kryptografische Signatur über die übergebenen Daten. Je nach Algorithmus entsteht dabei eine asymmetrische digitale Signatur (RSASSA-PKCS1-v1_5, RSA-PSS, ECDSA, Ed25519) mit einem privaten Schlüssel oder ein symmetrischer Message Authentication Code (HMAC) mit einem geheimen Schlüssel. Die Signatur prüft man anschließend mit crypto.subtle.verify().

Typische Einsatzfälle sind das Signieren von API-Anfragen (z. B. HMAC-Header für Webhooks), das Erzeugen selbst ausgestellter Tokens (JWT mit HS256 oder ES256), der Nachweis der Unversehrtheit von Daten, die über unsichere Kanäle übertragen oder in localStorage/IndexedDB gespeichert werden, sowie Challenge-Response-Verfahren.

Der verwendete CryptoKey muss zum Algorithmus passen und in seinen usages den Wert "sign" enthalten. Die Methode ist asynchron und liefert ein Promise, das mit einem ArrayBuffer erfüllt wird. Für die Übertragung wird dieser meist nach Base64, Base64url oder Hex umgewandelt.

Parameter

Name Typ Default Beschreibung
$algorithm Pflicht string | AlgorithmIdentifier | RsaPssParams | EcdsaParams Signaturalgorithmus. "RSASSA-PKCS1-v1_5", "HMAC" und "Ed25519" als String oder { name }; RSA-PSS braucht { name: "RSA-PSS", saltLength } (z. B. 32); ECDSA braucht { name: "ECDSA", hash: "SHA-256" }.
$key Pflicht CryptoKey Privater Schlüssel (asymmetrisch) bzw. geheimer Schlüssel (HMAC), dessen algorithm zum Parameter passt und dessen usages "sign" enthält.
$data Pflicht ArrayBuffer | TypedArray | DataView Die zu signierenden Daten als Binärpuffer, z. B. per new TextEncoder().encode(text) erzeugt.

Rückgabewert

Typ
Promise<ArrayBuffer>
Beschreibung
Ein Promise, das mit der Signatur als ArrayBuffer erfüllt wird. Bei ungültigem Schlüssel oder fehlender Nutzung "sign" wird es mit einer InvalidAccessError-DOMException abgelehnt.

Beispiele

HMAC-SHA256 für eine Webhook-Signatur

const encoder = new TextEncoder();

const toHex = (buffer) =>
  [...new Uint8Array(buffer)].map((b) => b.toString(16).padStart(2, '0')).join('');

const signPayload = async (secret, payload) => {
  const key = await crypto.subtle.importKey(
    'raw',
    encoder.encode(secret),
    { name: 'HMAC', hash: 'SHA-256' },
    false,
    ['sign']
  );
  const signature = await crypto.subtle.sign('HMAC', key, encoder.encode(payload));
  return toHex(signature);
};

const sig = await signPayload('mein-geheimnis', JSON.stringify({ event: 'order.paid', id: 42 }));
console.log(sig.length, sig);
64 <64-stelliger Hex-String>

ECDSA P-256: signieren und verifizieren

const { privateKey, publicKey } = await crypto.subtle.generateKey(
  { name: 'ECDSA', namedCurve: 'P-256' },
  true,
  ['sign', 'verify']
);

const data = new TextEncoder().encode('Vertrag Nr. 2024-17');
const algo = { name: 'ECDSA', hash: 'SHA-256' };

const signature = await crypto.subtle.sign(algo, privateKey, data);
console.log('Länge:', signature.byteLength);

const ok = await crypto.subtle.verify(algo, publicKey, signature, data);
console.log('Gültig:', ok);
Länge: 64 Gültig: true

RSA-PSS-Signatur als Base64

const { privateKey } = await crypto.subtle.generateKey(
  {
    name: 'RSA-PSS',
    modulusLength: 2048,
    publicExponent: new Uint8Array([1, 0, 1]),
    hash: 'SHA-256'
  },
  false,
  ['sign', 'verify']
);

const signature = await crypto.subtle.sign(
  { name: 'RSA-PSS', saltLength: 32 },
  privateKey,
  new TextEncoder().encode('Hallo Welt')
);

const base64 = btoa(String.fromCharCode(...new Uint8Array(signature)));
console.log(signature.byteLength, base64.slice(0, 16) + '…');
256 <Base64-Präfix>…

// Wichtig · Fallstricke

  • Nur in sicheren Kontexten: crypto.subtle gibt es ausschließlich unter HTTPS bzw. localhost. Unter HTTP ist es undefined.
  • Schlüsselnutzung: Fehlt \"sign\" in den usages oder wird ein öffentlicher Schlüssel übergeben, lehnt das Promise mit InvalidAccessError ab.
  • ECDSA-Format: Die Web Crypto API liefert ECDSA-Signaturen im rohen r||s-Format (IEEE P1363, 64 Byte bei P-256), nicht DER-kodiert wie OpenSSL oder Java. Für den Austausch mit solchen Systemen muss man umwandeln. Für JWT (ES256) passt das Rohformat direkt.
  • Nicht deterministisch: ECDSA und RSA-PSS nutzen Zufallswerte. Zwei Signaturen über dieselben Daten sehen also verschieden aus und sind trotzdem beide gültig. HMAC, RSASSA-PKCS1-v1_5 und Ed25519 sind deterministisch.
  • Vergleich: Prüfe Signaturen immer mit crypto.subtle.verify() und vergleiche keine Strings selbst. So vermeidest du Timing-Angriffe, und bei HMAC arbeitet der Vergleich in konstanter Zeit.
  • Ed25519 wird erst seit Kurzem überall unterstützt (Chrome 137, Firefox 129, Safari 17). Prüfe vorher, ob es verfügbar ist.
  • Kein Schutz im Client: Ein HMAC-Geheimnis im Browser-Code ist öffentlich. Signiere mit geheimen Schlüsseln nur auf dem Server oder mit nicht exportierbaren Schlüsseln (extractable: false), die in IndexedDB gespeichert sind.