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

SubtleCrypto.verify()

Methode

Prüft, ob eine digitale Signatur oder ein MAC zu den Daten und dem Schlüssel passt, und liefert ein <code>Promise&lt;boolean&gt;</code>.

seit JavaScript Baseline: alle modernen Browser (seit 2015), Node. Kategorie: webapi

Signatur

SubtleCrypto.prototype.verify(algorithm, key, signature, data)

Beschreibung

crypto.subtle.verify() ist das Gegenstück zu crypto.subtle.sign(). Die Methode prüft, ob eine Signatur mit dem angegebenen Schlüssel über genau diese Daten erzeugt wurde. Bei asymmetrischen Verfahren (RSASSA-PKCS1-v1_5, RSA-PSS, ECDSA, Ed25519) brauchen Sie dafür den öffentlichen Schlüssel. Bei HMAC verwenden Sie denselben geheimen Schlüssel, mit dem auch signiert wurde.

Typische Anwendungen sind die Prüfung von JWTs oder signierten Webhook-Nachrichten im Browser, in Service Workern oder in Edge-Runtimes. Auch die Echtheit heruntergeladener Daten oder signierter Konfigurationen lässt sich so prüfen, ohne eine externe Kryptobibliothek.

Das Ergebnis ist ein Promise, das mit true (Signatur gültig) oder false (ungültig) erfüllt wird. Eine ungültige Signatur löst keinen Fehler aus. Abgelehnt wird das Promise nur bei falscher Benutzung, etwa wenn der Schlüssel nicht zum Algorithmus passt oder nicht für "verify" freigegeben ist.

Die Parameter für den Algorithmus müssen genau denen beim Signieren entsprechen. Das gilt z. B. für die Hash-Funktion bei ECDSA und die saltLength bei RSA-PSS. Sonst schlägt die Prüfung stillschweigend mit false fehl.

Parameter

Name Typ Default Beschreibung
$algorithm Pflicht string | AlgorithmIdentifier | RsaPssParams | EcdsaParams Das Verfahren: "RSASSA-PKCS1-v1_5", { name: "RSA-PSS", saltLength }, { name: "ECDSA", hash }, "HMAC" oder "Ed25519". Es muss zum Signaturvorgang passen.
$key Pflicht CryptoKey Bei asymmetrischen Verfahren der öffentliche Schlüssel, bei HMAC der geheime Schlüssel. Der Schlüssel muss "verify" in usages enthalten.
$signature Pflicht ArrayBuffer | TypedArray | DataView Die zu prüfende Signatur als Binärdaten.
$data Pflicht ArrayBuffer | TypedArray | DataView Die ursprünglichen Daten, über die signiert wurde. Text vorher z. B. mit TextEncoder in Bytes umwandeln.

Rückgabewert

Typ
Promise<boolean>
Beschreibung
Wird mit true erfüllt, wenn die Signatur gültig ist, sonst mit false. Abgelehnt wird es mit InvalidAccessError, wenn der Schlüssel nicht zum Algorithmus passt oder nicht für die Prüfung freigegeben ist, und mit NotSupportedError bei einem unbekannten Algorithmus.

Beispiele

ECDSA: Nachricht signieren und prüfen

const encoder = new TextEncoder();
const data = encoder.encode('Bestellung #4711: 3 Artikel');

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

const algo = { name: 'ECDSA', hash: 'SHA-256' };
const signature = await crypto.subtle.sign(algo, privateKey, data);

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

// Manipulierte Daten
const tampered = encoder.encode('Bestellung #4711: 30 Artikel');
console.log('Manipuliert gültig:', await crypto.subtle.verify(algo, publicKey, signature, tampered));
Gültig: true Manipuliert gültig: false

HMAC-Webhook-Signatur prüfen (Hex-Signatur)

const hexToBytes = (hex) =>
  new Uint8Array(hex.match(/.{2}/g).map((b) => parseInt(b, 16)));

const verifyWebhook = async (secret, body, signatureHex) => {
  const enc = new TextEncoder();
  const key = await crypto.subtle.importKey(
    'raw',
    enc.encode(secret),
    { name: 'HMAC', hash: 'SHA-256' },
    false,
    ['verify']
  );
  return crypto.subtle.verify('HMAC', key, hexToBytes(signatureHex), enc.encode(body));
};

// Zum Testen: Signatur zuerst selbst erzeugen
const enc = new TextEncoder();
const signKey = await crypto.subtle.importKey(
  'raw', enc.encode('geheim'), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']
);
const body = '{"event":"payment.succeeded"}';
const sig = new Uint8Array(await crypto.subtle.sign('HMAC', signKey, enc.encode(body)));
const sigHex = [...sig].map((b) => b.toString(16).padStart(2, '0')).join('');

console.log(await verifyWebhook('geheim', body, sigHex));
console.log(await verifyWebhook('falsch', body, sigHex));
true false

JWT (RS256) mit öffentlichem JWK prüfen

const b64urlToBytes = (s) => {
  const b64 = s.replace(/-/g, '+').replace(/_/g, '/').padEnd(Math.ceil(s.length / 4) * 4, '=');
  return Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
};

const verifyJwtRS256 = async (token, publicJwk) => {
  const [header, payload, sig] = token.split('.');
  const key = await crypto.subtle.importKey(
    'jwk',
    publicJwk,
    { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' },
    false,
    ['verify']
  );
  const valid = await crypto.subtle.verify(
    'RSASSA-PKCS1-v1_5',
    key,
    b64urlToBytes(sig),
    new TextEncoder().encode(`${header}.${payload}`)
  );
  if (!valid) throw new Error('Ungültige Signatur');
  return JSON.parse(new TextDecoder().decode(b64urlToBytes(payload)));
};

// Aufruf: const claims = await verifyJwtRS256(token, jwkVomServer);

// Wichtig · Fallstricke

  • Nur in sicheren Kontexten: crypto.subtle gibt es nur über HTTPS oder auf localhost. Auf unsicheren Seiten ist es undefined.
  • Kein Fehler bei ungültiger Signatur: Prüfen Sie immer den booleschen Wert. Ein bloßes await ohne Auswertung heißt nicht, dass die Signatur gültig ist.
  • Parameter müssen übereinstimmen: Hash, saltLength (RSA-PSS) und Kurve müssen dieselben sein wie beim Signieren. Bei RSA ist der Hash an den Schlüssel gebunden und wird in importKey festgelegt. Bei ECDSA wird er im algorithm-Objekt von verify() angegeben.
  • Signaturformat bei ECDSA: Web Crypto erwartet das rohe Format r||s (IEEE P1363), bei P-256 also 64 Byte. OpenSSL, Java und Node-crypto.sign erzeugen standardmäßig DER-kodierte Signaturen. Diese müssen Sie erst umwandeln, sonst ist das Ergebnis immer false.
  • HMAC ohne eigenen Vergleich: Verwenden Sie verify(), statt selbst zu signieren und die Bytes zu vergleichen. verify() vergleicht in konstanter Zeit, ein eigener Vergleich mit === kann für Timing-Angriffe anfällig sein.
  • Text immer zuerst in Bytes umwandeln (TextEncoder). Schon Unterschiede bei Leerzeichen, Zeilenenden oder in der JSON-Serialisierung machen die Signatur ungültig. Prüfen Sie daher immer den rohen Anfrage-Body.
  • Ed25519 ist erst seit Kurzem in allen großen Browsern verfügbar (Chrome 137, Firefox 129, Safari 17). Prüfen Sie die Unterstützung, bevor Sie es einsetzen.
  • Der Schlüssel muss "verify" in seinen usages haben, sonst wird das Promise mit InvalidAccessError abgelehnt.