How to create a hash using Web Crypto API?
Asked Answered
C

3

7

I'm trying to create SHA-1 hash on the client-side. I'm trying to do this with Web Crypto API but when I'm comparing the output to what various online tools give me, the result is completely different. I think the problem is in ArrayBuffer to Hex conversion. Here is my code:

function generateHash() {
            var value = "mypassword";
            var crypto = window.crypto;
            var buffer = new ArrayBuffer(value);
            var hash_bytes = crypto.subtle.digest("SHA-1", buffer);
            hash_bytes.then(value => document.write([...new Uint8Array(value)].map(x => x.toString(16).padStart(2, '0')).join('')));
        }

Output of document.write should be:

91dfd9ddb4198affc5c194cd8ce6d338fde470e2

But it's not, I get a completely different hash of different length (should be 40). Could I have some advise on the problem? Thanks.

Colubrid answered 26/6, 2021 at 10:41 Comment(1)
Any advise guys?Colubrid
L
2

The problem seems to be more the input conversion from a string to an ArrayBuffer. E.g. with str2ab() the code works:

generateHash();

function generateHash() {
    var value = "mypassword";
    var crypto = window.crypto;
    var buffer = str2ab(value); // Fix
    var hash_bytes = crypto.subtle.digest("SHA-1", buffer);
    hash_bytes.then(value => document.write([...new Uint8Array(value)].map(x => x.toString(16).padStart(2, '0')).join('')));
}

// https://stackoverflow.com/a/11058858
function str2ab(str) {
    const buf = new ArrayBuffer(str.length);
    const bufView = new Uint8Array(buf);
    for (let i = 0, strLen = str.length; i < strLen; i++) {
        bufView[i] = str.charCodeAt(i);
    }
    return buf;
}

with the expected output:

91dfd9ddb4198affc5c194cd8ce6d338fde470e2
Leitman answered 26/6, 2021 at 11:13 Comment(0)
C
4

This is a basic function to generate a SHA-1 hex string digest for any input string:

async function digest(message, algo = 'SHA-1') {
  return Array.from(
    new Uint8Array(
      await crypto.subtle.digest(algo, new TextEncoder().encode(message))
    ),
    (byte) => byte.toString(16).padStart(2, '0')
  ).join('');
}
Celindaceline answered 31/10, 2023 at 18:50 Comment(1)
See the examples section of MDN for more developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/…Sensitivity
L
2

The problem seems to be more the input conversion from a string to an ArrayBuffer. E.g. with str2ab() the code works:

generateHash();

function generateHash() {
    var value = "mypassword";
    var crypto = window.crypto;
    var buffer = str2ab(value); // Fix
    var hash_bytes = crypto.subtle.digest("SHA-1", buffer);
    hash_bytes.then(value => document.write([...new Uint8Array(value)].map(x => x.toString(16).padStart(2, '0')).join('')));
}

// https://stackoverflow.com/a/11058858
function str2ab(str) {
    const buf = new ArrayBuffer(str.length);
    const bufView = new Uint8Array(buf);
    for (let i = 0, strLen = str.length; i < strLen; i++) {
        bufView[i] = str.charCodeAt(i);
    }
    return buf;
}

with the expected output:

91dfd9ddb4198affc5c194cd8ce6d338fde470e2
Leitman answered 26/6, 2021 at 11:13 Comment(0)
P
2

Using the debugger, it looks like var buffer = new ArrayBuffer(value); results in buffer being an empty ArrayBuffer. The text string stored in value must be utf-8 encoded in order to be correctly converted to bytes, which can then by passed as an input to the crypto.subtle.digest() function.

Try changing:

var buffer = new ArrayBuffer(value);

to:

var buffer = new TextEncoder("utf-8").encode(value);

This creates a Uint8Array (as expected by crypto.subtle.digest()) consisting of the bytes resulting from utf-8 encoding the text string in 'value'. This should solve the problem and produce the result that you are expecting.

Papist answered 24/8, 2021 at 20:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.