7

I am trying to Hash a text using HMAC SHA-256 in Javascript I have [secret Ket]

I have Ruby code to hash, but I need Javascript code to Hash the text

Ruby Code

OpenSSL::HMAC.hexdigest(
  'sha256', # hash function
  'HFgGgIOaLiyFgUhIjirOoqxloHuiLNr20jkhXrNw', # secret key (keep safe!)
  current_user.email # user's email address
)

Please suggest me for any solution.

Andreas
  • 21,535
  • 7
  • 47
  • 56
Rav
  • 103
  • 1
  • 1
  • 8
  • 4
    Possible duplicate of [Are there any SHA-256 javascript implementations that are generally considered trustworthy?](https://stackoverflow.com/questions/18338890/are-there-any-sha-256-javascript-implementations-that-are-generally-considered-t) – Gabriel Santos Mar 03 '18 at 07:46
  • 1
    browser or server side? – Jaromanda X Mar 03 '18 at 08:15

3 Answers3

16

With the Web crypto native api, taken from this source:

async function HMAC(key, message){
  const g = str => new Uint8Array([...unescape(encodeURIComponent(str))].map(c => c.charCodeAt(0))),
  k = g(key),
  m = g(message),
  c = await crypto.subtle.importKey('raw', k, { name: 'HMAC', hash: 'SHA-256' },true, ['sign']),
  s = await crypto.subtle.sign('HMAC', c, m);
  return btoa(String.fromCharCode(...new Uint8Array(s)))
}

/* TEST */
HMAC("mypassword", "Hello world!")
 .then(e => console.log(e))
 // jhg4cIbXnNicUx6BeG9EKEQHLp6NuBVzBp1d5D1Ghw4=
NVRM
  • 11,480
  • 1
  • 88
  • 87
  • 1
    This is really helpful! No external dependencies, just a tiny function to take care of it - thanks a lot man! – Jay Dadhania Jan 19 '21 at 09:57
  • 3
    Thanks, very helpful. I changed your function g() to use the TextEncoder. const encoder = new TextEncoder() const messageBytes = encoder.encode(message) – RodP Apr 18 '21 at 14:40
  • why is the js output different from the nodejs output? – Israel Obanijesu May 11 '21 at 08:06
  • Doesn't work, For instance try with message='a' and key='b'. This outputs 'y0SLRAxCrIrQhPyKh5XJj1t4AjWcMF6r1X7Nsg4kiJY=' instead of cb448b440c42ac8ad084fc8a8795c98f5b7802359c305eabd57ecdb20e248896 – KVM May 01 '22 at 10:33
4

I think CryptoJS would be able to do it using

CryptoJS.HmacSHA256(current_user.email, 'HFgGgIOaLiyFgUhIjirOoqxloHuiLNr20jkhXrNw') .toString(CryptoJS.enc.Hex)

Morten Olsen
  • 1,806
  • 14
  • 18
1

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
Advena
  • 1,664
  • 2
  • 24
  • 45