HMAC-generator

Bereken een HMAC-handtekening uit een bericht en een geheime sleutel, in hex en base64, en vergelijk hem met de handtekening die je ontvangen hebt. Alles draait in je browser — er wordt niets geüpload, opgeslagen of gelogd.

Gecontroleerd door het Berekening.be rekenteam
Bijgewerkt september 2026
Je sleutel en je bericht verlaten deze pagina nooit. Alles wordt in je browser berekend — er wordt niets geüpload, opgeslagen of gelogd, en een herlaad wist het.
Hexkleine letters
Base64standaardalfabet

Wat deze tool doet

Zet het bericht in het vak, de geheime sleutel eronder, kies de hash, en de handtekening verschijnt in hex en base64 terwijl je typt. Plak een handtekening die je ontvangen hebt in het laatste veld en de pagina vertelt je of hij overeenkomt met wat je sleutel en bericht opleveren.

Dat laatste is de reden dat deze pagina bestaat. Bijna elke keer dat iemand met de hand een HMAC nodig heeft, is diegene een webhook aan het debuggen die niet wil verifiëren, en de vraag is nooit "wat is HMAC" maar "welk van de vier dingen die ik verkeerd kon doen, heb ik verkeerd gedaan". De sleutelcodering of het algoritme kunnen veranderen en zien hoe het oordeel van rood naar groen omslaat, beantwoordt dat in seconden.

De sleutel kan als tekst, hex of base64 opgegeven worden, want aanbieders geven ze in alle drie de vormen uit, en dezelfde sleutel verkeerd gedecodeerd is een volledig andere sleutel. De bytetellingen naast de knoppen zijn er om dat te vangen: als een sleutel van 64 tekens als 64 bytes wordt getoond terwijl de aanbieder zegt dat het een sleutel van 32 bytes is, hash je de hex-tekenreeks in plaats van de bytes die hij vertegenwoordigt.

Wat HMAC is, en waarom niet gewoon de twee samen hashen

HMAC is een manier om twee dingen tegelijk te bewijzen: dat een bericht niet gewijzigd is, en dat het afkomstig is van iemand die de gedeelde sleutel bezit. Het neemt een hashfunctie en een sleutel, en produceert een handtekening van vaste lengte. Iedereen met de sleutel kan hem herberekenen; iemand zonder kan er geen geldige produceren voor een bericht dat hij zelf verzon.

De voor de hand liggende zelfgebouwde versie — de sleutel en het bericht samengevoegd hashen — is gebroken, en het is de moeite waard om te weten waarom, want de fout duikt nog steeds op in productiecode. Hashfuncties uit de MD5-, SHA-1- en SHA-2-families worden gebouwd door het bericht blok voor blok te absorberen en een lopende status bij te houden, en de hash is die status. Dus gegeven hash(secret + message) en de lengte van het geheim, kan een aanvaller verdergaan waar de functie stopte en hash(secret + message + extra) berekenen voor inhoud van zijn eigen keuze — zonder het geheim ooit te kennen. Dat is een length-extension attack, en het maakt van een handtekening een vervalsingsmachine.

HMAC bestaat om precies dat te voorkomen. Het hasht twee keer met twee afgeleide sleutels — een binnenste en een buitenste padding — zodat de hash die een aanvaller ziet nooit de interne status is van een hash die het geheim heeft verwerkt. Dit is waarom het antwoord op "zal ik mijn eigen MAC bouwen" nee is, en waarom elke taal een HMAC-functie in zijn standaardbibliotheek heeft.

Het algoritme kiezen

HMAC-SHA-256 is de standaard en het juiste antwoord tenzij iets anders voorschrijft. Het is wat Stripe, GitHub, Slack, Shopify en de meeste anderen gebruiken voor webhook-handtekeningen, en het is wat je in je eigen API's zou moeten grijpen.

HMAC-SHA-512 geeft een langere handtekening en is vaak sneller op 64-bit hardware; er is geen veiligheidsreden om het te verkiezen, en ook geen reden om het te vermijden. HMAC-SHA-384 bestaat vooral omdat bepaalde standaarden het voorschrijven.

HMAC-SHA-1 is een bijzonder geval dat de moeite waard is om te begrijpen. SHA-1 is gebroken voor botsingen, wat het uitsluit voor handtekeningen en certificaten — maar HMAC hangt niet op dezelfde manier af van botsingsweerstand, dus HMAC-SHA-1 wordt niet als praktisch gebroken beschouwd en blijft breed in gebruik, met name in AWS Signature Version 2 en oudere OAuth. Bestaande systemen die het gebruiken staan niet in brand; nieuwe systemen zouden toch SHA-256 moeten gebruiken.

Je zult merken dat MD5 niet wordt aangeboden. HMAC-MD5 heeft dezelfde theoretische status als HMAC-SHA-1, maar browsers bieden bewust geen MD5 aan, en er is geen goede reden om er iets nieuws op te bouwen.

De sleutel bestaat uit bytes, niet uit een string

Hier komen de meeste mismatches vandaan. HMAC ondertekent bytes met bytes; een sleutel geschreven als tekst moet naar bytes omgezet worden, en dat kan op meer dan één manier.

Een webhookgeheim zoals whsec_abc123 wordt normaal gebruikt als letterlijke UTF-8-tekst — de bytes van de tekens, precies zoals afgedrukt. Maar een sleutel die je als 64 hex-tekens gegeven werd, is vrijwel zeker een sleutel van 32 bytes opgeschreven in hex, en de 64 tekens als tekst hashen geeft een andere, foute handtekening. Hetzelfde geldt voor base64: een sleutel zoals c2VjcmV0… is misschien bedoeld als de bytes waarnaar hij decodeert, niet als de string. De documentatie van de aanbieder is de enige autoriteit over welke van de twee het is, en de bytetelling op deze pagina is de snelste manier om te controleren of je die goed gelezen hebt.

Lengte is veel minder interessant dan codering. HMAC accepteert sleutels van elke lengte: korter dan de blokgrootte van de hash en ze worden opgevuld, langer en ze worden eerst gehasht. Een sleutel van minstens de lengte van de hash — 32 bytes voor SHA-256 — is een verstandige ondergrens, en er is geen voordeel voorbij de blokgrootte. Wat veel meer telt, is dat de sleutel willekeurig en geheim is: een gedenkwaardige wachtwoordzin gebruikt als HMAC-sleutel is een wachtwoordzin die een aanvaller offline kan raden, op snelheid, zonder ratelimiet.

Een webhook-handtekening verifiëren

Het patroon is hetzelfde bij elke aanbieder. Ze berekenen een HMAC van de request body met de sleutel die jullie beiden bezitten, en sturen die in een header — X-Hub-Signature-256, Stripe-Signature, X-Signature. Jouw taak is hetzelfde te berekenen en te vergelijken.

Drie details bepalen of het werkt. Ten eerste, onderteken de ruwe body, niet een opnieuw geserialiseerde versie ervan: JSON parsen en weer dumpen verandert witruimte en sleutelvolgorde, en de handtekening gaat over bytes. De meeste frameworks maken de ruwe body beschikbaar maar stoppen daarmee zodra een body-parser gedraaid heeft, wat de meest voorkomende oorzaak is van een webhook die niet wil verifiëren.

Ten tweede, onderteken exact wat zij ondertekend hebben. Verschillende aanbieders ondertekenen niet alleen de body maar een samengestelde string — Stripe ondertekent timestamp.payload, anderen voegen een versie-prefix toe. Als de documentatie een sjabloon toont, reproduceer het dan teken voor teken, inclusief het scheidingsteken.

Ten derde, vergelijk in constante tijd. hash_equals() in PHP, crypto.timingSafeEqual in Node, hmac.compare_digest in Python. Een normale stringvergelijking stopt zodra twee tekens verschillen, en dat tijdsverschil is genoeg om, over veel pogingen, een aanvaller een geldige handtekening byte voor byte te laten ontdekken. De vergelijking op deze pagina is een gewone, wat prima is omdat het op jouw eigen scherm gebeurt zonder dat iemand het timet — in je servercode is dat niet zo.

En controleer de tijdstempel als er een is. Een handtekening blijft voor altijd geldig, dus een herhaald request met een oude maar echte handtekening zal verifiëren, tenzij je alles ouder dan een paar minuten afwijst.

Waarom de handtekening niet overeenkomt

In ruwe volgorde van hoe vaak elke schuldige blijkt te zijn: de body is geparst en opnieuw geserialiseerd vóór het ondertekenen; de sleutel is met de verkeerde codering gedecodeerd; het bericht bevatte een regeleinde extra of miste er een; de uitvoer werd vergeleken als hex tegen een base64-handtekening of andersom; de aanbieder ondertekent een samengestelde string in plaats van de body; de header draagt een prefix zoals sha256= die niet verwijderd werd; of de verkeerde sleutel werd gebruikt, omdat de test- en live-geheimen op elkaar lijken.

Deze pagina is gebouwd om dat te isoleren. Plak de body, plak de sleutel, plak de handtekening en verander één ding tegelijk. Wissel de sleutelcodering tussen tekst en hex en zie de hex-uitvoer volledig veranderen — als een van beide overeenkomt met de handtekening die je ontving, was dat je bug. Vergelijk de bytetellingen met wat de aanbieder zegt dat de groottes zouden moeten zijn. En als een hex-vergelijking faalt, werp dan een blik op de base64-rij voordat je aanneemt dat de sleutel fout is.

Het prefix-geval wordt voor je afgehandeld: een handtekening geplakt als sha256=abc… wordt vergeleken zonder de prefix, want zo sturen GitHub en verschillende anderen het.

Je sleutel verlaat je browser nooit

Alles op deze pagina wordt lokaal berekend, met de eigen crypto.subtle-implementatie van je browser. De sleutel en het bericht worden nooit geüpload, nooit in een cookie of lokale opslag geschreven, en nooit gelogd. Het herladen van de pagina wist beide.

Voor deze tool is dat geen leuke bijkomstigheid, het is het enige dat hem bruikbaar maakt. Een webhookgeheim is een levende credential: iedereen die het bezit kan requests vervalsen die jouw systeem als echt accepteert. Een sleutel plakken in een server-side HMAC-tool betekent een werkende credential aan een vreemde overhandigen en hopen — en geen enkele belofte op de pagina is van buitenaf te controleren. Hier kun je het wel controleren: open je netwerktabblad en typ. Afgezien van de analytics-beacon die deze site bij elke paginalading verstuurt — een URL en een paginatitel, net als elke andere pagina hier — wordt er niets verstuurd. Verbreek de netwerkverbinding volledig en de handtekening wordt nog steeds berekend.

Behandel een sleutel die je ergens geplakt hebt toch met de voorzichtigheid die de situatie verdient. Als een geheim door een browser is gegaan op een gedeelde machine, of je weet niet zeker welke tool je de vorige keer gebruikte, kost roteren een minuut en maakt een einde aan de vraag.

Een uitgewerkt voorbeeld

Typ The quick brown fox jumps over the lazy dog als bericht, key als sleutel, laat de codering op tekst staan en het algoritme op SHA-256. De hex-rij toont f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8, wat de gepubliceerde testvector is voor dat paar — dezelfde waarde die elke correcte implementatie oplevert, en een snelle manier om te bevestigen dat deze pagina het eens is met je eigen code.

Wissel nu het algoritme naar SHA-1 en de handtekening verandert volledig in plaats van in te korten vanaf hetzelfde begin; wissel de sleutelcodering naar hex en je krijgt een foutmelding, want key is geen geldige hex. Plak de SHA-256-waarde in het laatste veld, zet het algoritme terug, en het oordeel wordt groen. Verander één teken van het bericht en het wordt rood — precies het gedrag waar een webhook-ontvanger op vertrouwt.

Nee. De sleutel en het bericht worden gebruikt door de eigen crypto-implementatie van je browser en blijven in de pagina. Er is geen upload, geen cookie, geen lokale opslag en geen logging, wat ook waarom een herlaad beide velden leegt.

HMAC-SHA-256, tenzij het systeem waarmee je praat een andere voorschrijft. SHA-512 is even goed en vaak sneller op 64-bit hardware. HMAC-SHA-1 is in de praktijk nog steeds veilig ondanks dat SHA-1 gebroken is voor botsingen, maar het hoort bij bestaande systemen, niet bij nieuwe.

Meestal verschillen het bericht of de sleutel door iets onzichtbaars. Een regeleinde extra, een opnieuw geserialiseerde JSON-body, een sleutel die als tekst gedecodeerd werd terwijl hij hex was, of een handtekening die in het verkeerde uitvoerformaat vergeleken werd. Controleer de bytetellingen hier tegen wat je code werkelijk doorgeeft.

Nee. Het bewijst integriteit en authenticiteit, het verbergt niets — het bericht wordt niet verborgen door het te ondertekenen. Als de inhoud ook geheim moet blijven, versleutel het dan, en geef de voorkeur aan een geauthenticeerde modus zoals AES-GCM die beide in één bewerking biedt.

Minstens zo lang als de hash — 32 bytes voor SHA-256 — en willekeurig gegenereerd in plaats van gekozen. Alles voorbij de blokgrootte van de hash (64 bytes voor SHA-256) voegt niets toe, want langere sleutels worden eerst gehasht. Willekeur is veel belangrijker dan lengte.

Niet in servercode. Een normale vergelijking stopt bij de eerste afwijkende byte, en dat tijdsverschil kan gemeten en misbruikt worden over veel requests. Gebruik hash_equals(), crypto.timingSafeEqual() of hmac.compare_digest(). De vergelijking op deze pagina is een gewone, omdat ze op je eigen machine draait zonder dat een aanvaller kan timen.

Browsers implementeren bewust geen MD5 in hun cryptografie-interface. HMAC-MD5 wordt niet als praktisch gebroken beschouwd op de manier waarop MD5-handtekeningen dat zijn, maar er is geen reden om er iets nieuws mee te beginnen, en genoeg reden om dat niet te doen.

Was deze rekenmachine nuttig?

Tik op een ster om te beoordelen. Uw feedback helpt ons de tools te verbeteren waar mensen het meest op vertrouwen.