From @KVM's comment on @NVRM's answer, their approach returns the hash in Base64 format, instead of HEX.
Moreover, they use a somehow questionable approach for converting a String
to an ArrayBuffer
(which may have limitations).
A better approach would be with the native Web API TextEncoder
. I've created an alternative method that uses TextEncoder
and returns the value in hex instead Base64.
async function hmac(secretKey, message, algorithm = "SHA-256") {
// Convert the message and secretKey to Uint8Array
const encoder = new TextEncoder();
const messageUint8Array = encoder.encode(message);
const keyUint8Array = encoder.encode(secretKey);
// Import the secretKey as a CryptoKey
const cryptoKey = await window.crypto.subtle.importKey(
"raw",
keyUint8Array,
{ name: "HMAC", hash: algorithm },
false,
["sign"]
);
// Sign the message with HMAC and the CryptoKey
const signature = await window.crypto.subtle.sign(
"HMAC",
cryptoKey,
messageUint8Array
);
// Convert the signature ArrayBuffer to a hex string
const hashArray = Array.from(new Uint8Array(signature));
const hashHex = hashArray
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
return hashHex;
}
// Example
const mySecretKey = "b";
const myMessage = "a";
hmac(mySecretKey, myMessage).then(h=>console.log(h)); // cb448b440c42ac8ad084fc8a8795c98f5b7802359c305eabd57ecdb20e248896