How can I hash a string with SHA256 in JS?
Solution 1
2021 update - SHA256 is now included in current browsers
As you mention in your question, you don't need custom Crypto implementations to do this.
WebCrypto is supported in all current browsers. Use window.crypto.subtle.digest
to make a SHA 256 hash.
const digest = await window.crypto.subtle.digest('SHA-256', data);
If you want something asynchronous, sha.js
for example has 12.8 million downloads a month, and is actively maintained.
const digest = shajs('sha256').update(data).digest('hex')
Solution 2
Hellow there :D it's quite a function. If you are a scholar, you would like to check this article: https://www.movable-type.co.uk/scripts/sha256.html
Pure javascript:
var sha256 = function sha256(ascii) {
function rightRotate(value, amount) {
return (value>>>amount) | (value<<(32 - amount));
};
var mathPow = Math.pow;
var maxWord = mathPow(2, 32);
var lengthProperty = 'length'
var i, j; // Used as a counter across the whole file
var result = ''
var words = [];
var asciiBitLength = ascii[lengthProperty]*8;
//* caching results is optional - remove/add slash from front of this line to toggle
// Initial hash value: first 32 bits of the fractional parts of the square roots of the first 8 primes
// (we actually calculate the first 64, but extra values are just ignored)
var hash = sha256.h = sha256.h || [];
// Round constants: first 32 bits of the fractional parts of the cube roots of the first 64 primes
var k = sha256.k = sha256.k || [];
var primeCounter = k[lengthProperty];
/*/
var hash = [], k = [];
var primeCounter = 0;
//*/
var isComposite = {};
for (var candidate = 2; primeCounter < 64; candidate++) {
if (!isComposite[candidate]) {
for (i = 0; i < 313; i += candidate) {
isComposite[i] = candidate;
}
hash[primeCounter] = (mathPow(candidate, .5)*maxWord)|0;
k[primeCounter++] = (mathPow(candidate, 1/3)*maxWord)|0;
}
}
ascii += '\x80' // Append Ƈ' bit (plus zero padding)
while (ascii[lengthProperty]%64 - 56) ascii += '\x00' // More zero padding
for (i = 0; i < ascii[lengthProperty]; i++) {
j = ascii.charCodeAt(i);
if (j>>8) return; // ASCII check: only accept characters in range 0-255
words[i>>2] |= j << ((3 - i)%4)*8;
}
words[words[lengthProperty]] = ((asciiBitLength/maxWord)|0);
words[words[lengthProperty]] = (asciiBitLength)
// process each chunk
for (j = 0; j < words[lengthProperty];) {
var w = words.slice(j, j += 16); // The message is expanded into 64 words as part of the iteration
var oldHash = hash;
// This is now the undefinedworking hash", often labelled as variables a...g
// (we have to truncate as well, otherwise extra entries at the end accumulate
hash = hash.slice(0, 8);
for (i = 0; i < 64; i++) {
var i2 = i + j;
// Expand the message into 64 words
// Used below if
var w15 = w[i - 15], w2 = w[i - 2];
// Iterate
var a = hash[0], e = hash[4];
var temp1 = hash[7]
+ (rightRotate(e, 6) ^ rightRotate(e, 11) ^ rightRotate(e, 25)) // S1
+ ((e&hash[5])^((~e)&hash[6])) // ch
+ k[i]
// Expand the message schedule if needed
+ (w[i] = (i < 16) ? w[i] : (
w[i - 16]
+ (rightRotate(w15, 7) ^ rightRotate(w15, 18) ^ (w15>>>3)) // s0
+ w[i - 7]
+ (rightRotate(w2, 17) ^ rightRotate(w2, 19) ^ (w2>>>10)) // s1
)|0
);
// This is only used once, so *could* be moved below, but it only saves 4 bytes and makes things unreadble
var temp2 = (rightRotate(a, 2) ^ rightRotate(a, 13) ^ rightRotate(a, 22)) // S0
+ ((a&hash[1])^(a&hash[2])^(hash[1]&hash[2])); // maj
hash = [(temp1 + temp2)|0].concat(hash); // We don't bother trimming off the extra ones, they're harmless as long as we're truncating when we do the slice()
hash[4] = (hash[4] + temp1)|0;
}
for (i = 0; i < 8; i++) {
hash[i] = (hash[i] + oldHash[i])|0;
}
}
for (i = 0; i < 8; i++) {
for (j = 3; j + 1; j--) {
var b = (hash[i]>>(j*8))&255;
result += ((b < 16) ? 0 : '') + b.toString(16);
}
}
return result;
};
Source: https://geraintluff.github.io/sha256/
Solution 3
Checkout this: https://github.com/brix/crypto-js
You can use the following:
require(["crypto-js/aes", "crypto-js/sha256"], function (AES, SHA256)
{
console.log(SHA256("Message"));
});
or without require:
<script type="text/javascript" src="path-to/bower_components/crypto-js/crypto-js.js"></script>
<script type="text/javascript">
var encrypted = CryptoJS.AES(...);
var encrypted = CryptoJS.SHA256(...);
</script>
Solution 4
Pure javascript, no dependencies needed
You can use SubtleCrypto.digest() to help you.
It needs a Uint8Array
If your data is a Blob
const blob = new Blob([file])
const arrayBuffer = await blob.arrayBuffer()
const uint8Array = new Uint8Array(arrayBuffer)
SubtleCrypto.digest("SHA-256", uint8Array)
If data is string, use TextEncoder.encode() convert to Uint8Array
const uint8Array = new TextEncoder().encode(data)
SubtleCrypto.digest("SHA-256", uint8Array)
Below is a runnable example for your reference.
<input type="file" multiple/>
<input placeholder="Press `Enter` when done."/>
<script>
/**
* @param {"SHA-1"|"SHA-256"|"SHA-384"|"SHA-512"} algorithm https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest
* @param {string|Blob} data
*/
async function getHash(algorithm, data) {
const main = async (msgUint8) => { // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest#converting_a_digest_to_a_hex_string
const hashBuffer = await crypto.subtle.digest(algorithm, msgUint8)
const hashArray = Array.from(new Uint8Array(hashBuffer))
return hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); // convert bytes to hex string
}
if (data instanceof Blob) {
const arrayBuffer = await data.arrayBuffer()
const msgUint8 = new Uint8Array(arrayBuffer)
return await main(msgUint8)
}
const encoder = new TextEncoder()
const msgUint8 = encoder.encode(data)
return await main(msgUint8)
}
const inputFile = document.querySelector(`input[type="file"]`)
const inputText = document.querySelector(`input[placeholder^="Press"]`)
inputFile.onchange = async (event) => {
for (const file of event.target.files) {
console.log(file.name, file.type, file.size + "bytes")
const hashHex = await getHash("SHA-256", new Blob([file]))
console.log(hashHex)
}
}
inputText.onkeyup = async (keyboardEvent) => {
if (keyboardEvent.key === "Enter") {
const hashHex = await getHash("SHA-256", keyboardEvent.target.value)
console.log(hashHex)
}
}
</script>
Solution 5
Cannot read property 'digest' of undefined
when calling crypto.subtle.digest
implies that subtle
is not available within crypto
; therefore, digest
can't possibly exist, as its containing module does not exist.
Logically then, crypto.subtle
must not be available in this scope, and in fact, that holds true in the browser anywhere outside of a secure context.
When is a context considered secure? — developer.mozilla.org
A context will be considered secure when it's delivered securely (or locally), and when it cannot be used to provide access to secure APIs to a context that is not secure. In practice, this means that for a page to have a secure context, it and all the pages along its parent and opener chain must have been delivered securely.
For example, a page delivered securely over TLS is not considered a secure context if it has a parent or ancestor document that was not delivered securely; otherwise, the page would then be able to expose sensitive APIs to the non-securely delivered ancestor via postMessage messages. Similarly, if a TLS-delivered document is opened in a new window by an insecure context without noopener being specified, then the opened window is not considered to be a secure context (since the opener and the opened window could communicate via postMessage).
Locally delivered files such as http:// localhost* and file:// paths are considered to have been delivered securely.
¦¦¦¦¦ Contexts that are not local must be served over https:// or wss:// and where the protocols used should not be considered deprecated.
In a secure context, your code works perfectly 😄
1: Secure contexts - Web security | MDN
2: When is a context considered secure? - Secure contexts - Web security | MDN
3: Window.postMessage() - Web APIs | MDN
4: Window functionality features - Window.open() - Web APIs | MDN
Federico Fusco
I'm a student, still learning the ways of the computer world. I hope to expand that knowledge on this website and help others improve their abilities as well! I'm learning JS as well as PHP and a few other languages.
Updated on July 05, 2022Comments
-
Federico Fusco almost 2 years
Description
I'm looking to hash a string locally with SHA256 in Javascript. I've been looking around thinking there would be some sort of official library or function, but all I found were loads of different projects, each with different scripts, and I'm not so sure scripts to trust (as I'm not an expert and definitely not qualified to evaluate them) or how to implement them. EDIT: I need the output in text, not hexes, sorry if I didn't explain that when posting the original question.
Code
Here's what I've tried so far:
async function sha256(message) { // encode as UTF-8 const msgBuffer = new TextEncoder('utf-8').encode(message); // hash the message const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer); // convert ArrayBuffer to Array const hashArray = Array.from(new Uint8Array(hashBuffer)); // convert bytes to hex string const hashHex = hashArray.map(b => ('00' + b.toString(16)).slice(-2)).join(''); console.log(hashHex); return hashHex; } sha256(passwordInput);
Console Output:
Uncaught (in promise) TypeError: Cannot read property 'digest' of undefined
I'm new to javascript and I'm open to all suggestions, so yeah.
Update
Although most of your suggestions work, for those of you who are looking to use the Web Crypto API, the answer was on line #5. I needed to change
crypto.subtle.digest
towindow.crypto.subtle.digest
-
Admin over 4 yearsWhat browser (including version) are you using?
-
Federico Fusco over 4 years@Amy I'm using Google Chrome Version 79.0.3945.130 (64-bit), hope this helps
-
mikemaccana about 3 yearsYou're right - re
window.crypto
- I've added that as an answer (before I noticed you'd written the same thing in your update). If you still use StackOverflow, can you update the answer? -
jordanfb over 2 yearsBe aware that
crypto.subtle.digest
is not suitable if you're trying to hash a large file (like 1GB+). Currently, you have to resort to 3rd-party libs. -
geralds over 2 yearsThis worked for me, but I had problems doing anything with the returned value of hashHex, such as printing it to the screen. My solution was to retrieve the hash value hashhex from the console log ONLY: for local use, this is perfectly adequate.
-
-
Federico Fusco over 4 yearsThanks for the answer (it works), but I need the output to be in text format, not hex format, sorry if I didn't make that clear in the description of the problem.
-
Gen4ik over 4 yearsI think it is a text format (it just looks like hex).
-
Albert Renshaw about 4 yearsI can't figure out why people are you downvoting you? Your function works and with no dependancies
-
Mr_Antivius about 4 yearsI didn't downvote, but I believe he was down voted because in the world of cryptography, it's highly advised to not "roll your own" crypto as it isn't vetted and could have security issues. It's usually best to go with a well-known library as it gives you the best security and, usually, constant updates.
-
David ROUMANET almost 4 yearsI think this is great for educational purposes, thanks a lot.
-
mikemaccana about 3 yearsBecause copy pasting code from Stack Overflow is a dependency. The copy pasted code is just an unmanaged dependency, where there's little bug reporting and no centralised update mechanism. The code is definitely good for education purposes but I'm concerned that copy pasting code is being presented as an alternative to using a library.
-
Ser almost 3 yearsBe careful, in Chrome 60, they added a feature that disables crypto.subtle for non-TLS connections. See stackoverflow.com/a/46671627/2294168
-
carloswm85 over 2 yearsI didn't use this answer, but it lead me in the right directions. Here's some other information I found thanks to it: Web Cryptography API: developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest
-
jordanfb over 2 yearsNative is async too
await crypto.subtle.digest()
. Returns a Promise (see developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/…) -
Nick Vu about 2 yearsWhile this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - From Review
-
djdance about 2 yearsattention, data is not a string, and returned data is not a string too (it's a promise), to do convert and async use example in the Mozilla's link above
-
mikemaccana about 2 years@djdance if you mean you have a Promise and want to get the value inside the Promise, you’ll need to use
await
.