Verification

Our public key

Every certificate is signed by VerifiedWallet. This key lets anyone check that signature: a bank, an auditor, a counterparty or you. No account, and no need to trust us.

What it does

When we issue a certificate, our server signs it with a private key that only it holds. This page publishes the matching public key. A public key can't create signatures, only check them, so it's safe to share with everyone.

If a certificate has been changed in any way (a different wallet, tier or result), or wasn't made by us, the check fails. Every certificate page runs this check for you: it's the Attester signature valid tick. You can also save the key and check certificates offline, even if this site were ever down.

The key

Key IDLoading…

Each certificate names the key that signed it (its kid). It should match this ID, or one of the earlier keys listed below.

AlgorithmES256 (ECDSA with the P-256 curve and SHA-256)
Loading…

Check a certificate yourself

For technical readers: paste this into your browser's developer console (or run it with Node 20+), with the certificate ID filled in. It fetches the certificate and our key, and checks the signature with your browser's own cryptography.

const id = "VW-XXXX-XXXX"; // the certificate to check
const cert = await (await fetch(`https://verifiedwallet.io/api/cert/${id}`)).json();
const { publicKey } = await (await fetch("https://verifiedwallet.io/api/attester")).json();
// The signed data is the certificate's core, with its fields in alphabetical order.
const sort = (v) => Array.isArray(v) ? v.map(sort)
  : v && typeof v === "object" ? Object.fromEntries(Object.keys(v).sort().map((k) => [k, sort(v[k])])) : v;
const key = await crypto.subtle.importKey("jwk", publicKey, { name: "ECDSA", namedCurve: "P-256" }, false, ["verify"]);
const sig = Uint8Array.from(atob(cert.attestation.sig.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0));
const ok = await crypto.subtle.verify({ name: "ECDSA", hash: "SHA-256" }, key, sig,
  new TextEncoder().encode(JSON.stringify(sort(cert.core))));
console.log(ok ? "Valid: signed by VerifiedWallet" : "NOT valid");

The certificate's own wallet signature (made by the wallet holder) is checked separately, with the wallet's own scheme; the certificate page does that too. More in the guide How to check a wallet certificate.

If the key ever changes

If we ever replace the key, the new one appears here and the old one moves to Earlier keys, so certificates signed before the change keep checking out. We'll never sign certificates with a key that isn't published on this page.