Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get HMAC with Crypto Web API

How can I get HMAC-SHA512(key, data) in the browser using Crypto Web API (window.crypto)?

Currently I am using CryptoJS library and it is pretty simple:

CryptoJS.HmacSHA512("myawesomedata", "mysecretkey").toString();

Result is 91c14b8d3bcd48be0488bfb8d96d52db6e5f07e5fc677ced2c12916dc87580961f422f9543c786eebfb5797bc3febf796b929efac5c83b4ec69228927f21a03a.

I want to get rid of extra dependencies and start using Crypto Web API instead. How can I get the same result with it?

like image 869
Stepan Snigirev Avatar asked Nov 16 '17 12:11

Stepan Snigirev


2 Answers

Answering my own question. The code below returns the same result as CryptoJS.HmacSHA512("myawesomedata", "mysecretkey").toString();

There are promises everywhere as WebCrypto is asynchronous:

// encoder to convert string to Uint8Array
var enc = new TextEncoder("utf-8");

window.crypto.subtle.importKey(
    "raw", // raw format of the key - should be Uint8Array
    enc.encode("mysecretkey"),
    { // algorithm details
        name: "HMAC",
        hash: {name: "SHA-512"}
    },
    false, // export = false
    ["sign", "verify"] // what this key can do
).then( key => {
    window.crypto.subtle.sign(
        "HMAC",
        key,
        enc.encode("myawesomedata")
    ).then(signature => {
        var b = new Uint8Array(signature);
        var str = Array.prototype.map.call(b, x => x.toString(16).padStart(2, '0')).join("")
        console.log(str);
    });
});
like image 92
Stepan Snigirev Avatar answered Oct 31 '22 13:10

Stepan Snigirev


Async/Await Crypto Subtle HMAC SHA-256/512 with Base64 Digest

The following is a copy of the ✅ answer. This time we are using async/await for clean syntax. This approach also offers a base64 encoded digest.

  • secret is the secret key that will be used to sign the body.
  • body is the string-to-sign.
  • enc is a text encoder that converts the UTF-8 to JavaScript byte arrays.
  • algorithm is a JS object which is used to identify the signature methods.
  • key is a CryptoKey.
  • signature is the byte array hash.
  • digest is the base64 encoded signature.

The JavaScript code follows:

(async ()=>{
'use strict';

let secret = "sec-demo"; // the secret key
let enc = new TextEncoder("utf-8");
let body = "GET\npub-demo\n/v2/auth/grant/sub-key/sub-demo\nauth=myAuthKey&g=1&target-uuid=user-1&timestamp=1595619509&ttl=300";
let algorithm = { name: "HMAC", hash: "SHA-256" };

let key = await crypto.subtle.importKey("raw", enc.encode(secret), algorithm, false, ["sign", "verify"]);
let signature = await crypto.subtle.sign(algorithm.name, key, enc.encode(body));
let digest = btoa(String.fromCharCode(...new Uint8Array(signature)));

console.log(digest);

})();

The original answer on this page was helpful in a debugging effort earlier today. We're using it to help identify a bug in our documentation for creating signatures for granting access tokens to use APIs with read/write permissions.

like image 24
Stephen Blum Avatar answered Oct 31 '22 13:10

Stephen Blum