如何在 JS 中使用 SHA256 散列字符串?
How can I hash a string with SHA256 in JS?
描述
我希望在 Javascript 中使用 SHA256 在本地散列字符串。
我一直在四处寻找,认为会有某种官方图书馆或功能,
但我发现的是大量不同的项目,每个项目都有不同的脚本,而且我不太确定脚本是否值得信任(因为我不是专家,也绝对没有资格评估它们)或如何实施它们。
编辑:我需要文本输出,而不是十六进制,如果我在发布原始问题时没有解释,抱歉。
代码
这是我到目前为止尝试过的方法:
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);
控制台输出:
Uncaught (in promise) TypeError: Cannot read property 'digest' of undefined
我是 javascript 的新手,我愿意接受所有建议,所以是的。
更新
虽然您的大部分建议都有效,但对于那些希望使用 Web Crypto API 的人来说,答案在第 5 行。我需要将 crypto.subtle.digest
更改为 window.crypto.subtle.digest
检查这个:https://github.com/brix/crypto-js
您可以使用以下内容:
require(["crypto-js/aes", "crypto-js/sha256"], function (AES, SHA256)
{
console.log(SHA256("Message"));
});
或不要求:
<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>
你好 :D 这是一个很好的功能。
如果你是学者,你想看看这篇文章:https://www.movable-type.co.uk/scripts/sha256.html
纯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;
};
Cannot read property 'digest' of undefined
调用 crypto.subtle.digest
意味着 subtle
在 crypto
内不可用;因此,digest
不可能存在,因为它的包含模块不存在。
那么从逻辑上讲,crypto.subtle
一定不能在此范围内使用,事实上,在浏览器中也是如此 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.
在安全环境下,您的代码可以完美运行
1: 安全上下文 - Web security | MDN
2: 什么时候上下文被认为是安全的? - Secure contexts - Web security | MDN
3: Window.postMessage() - Web APIs | MDN
4: Window 功能特性 - Window.open() - Web APIs | MDN
代码没问题。
最后一行应如下所示:
var vDigest = await sha256(passwordInput);
上面来自 ofundefined 的答案有一堆缺少分号和错误。我已经清理了代码,因此您可以将其作为函数调用。我不确定这是否适用于 unicode 字符。可能必须将 unicode 转换为正常的 ascii 才能工作。但是,正如链接文章所说......这不应该在生产环境中使用。此外,此代码的原始版本似乎确实支持 unicode,因此使用它可能比使用此功能更好。
上面的文章(似乎支持 unicode)显示了这个函数,它与下面发布的函数不同...
https://www.movable-type.co.uk/scripts/sha256.html
如何使用
console.log(sha256('Perry Computer Services'));
输出
89bae4aeb761e42cb71ba6b62305e0980154cf21992c9ab2ab6fc40966ab5bf3
使用 PHP 哈希测试 - 输出
89bae4aeb761e42cb71ba6b62305e0980154cf21992c9ab2ab6fc40966ab5bf3
这不是上面其他链接页面上显示的完整功能。正如您从上面的示例中看到的那样,它确实可以“按原样”处理非 unicode 字符。
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;
};
2021 年更新 - SHA256 现在包含在当前浏览器中
正如您在问题中提到的,您不需要自定义加密实现来执行此操作。
WebCrypto is supported in all current browsers。使用 window.crypto.subtle.digest
生成 SHA 256 哈希值。
const digest = await window.crypto.subtle.digest('SHA-256', data);
如果你想要一些异步的东西,sha.js
例如每月有 1280 万次下载,并且正在积极维护。
const digest = shajs('sha256').update(data).digest('hex')
纯 javascript,不需要依赖项
您可以使用SubtleCrypto.digest()来帮助您。
它需要一个Uint8Array
如果您的数据是 Blob
const blob = new Blob([file])
const arrayBuffer = await blob.arrayBuffer()
const uint8Array = new Uint8Array(arrayBuffer)
SubtleCrypto.digest("SHA-256", uint8Array)
如果数据是字符串,使用TextEncoder.encode()转换为Uint8Array
const uint8Array = new TextEncoder().encode(data)
SubtleCrypto.digest("SHA-256", uint8Array)
下面是一个可运行的例子,供您参考。
<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>
另请参阅
您可以使用 CDN 库。
https://cdnjs.com/libraries/crypto-js
请将以下脚本标记引用到您的 html :
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js" integrity="sha512-E8QSvWZ0eCLGk4km3hxSsNmGWbLtSCSUcewDQPQWZF6pEU8GlT8a5fF32wOl1i8ftdMhssTrF/OhyGWwonTcXA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
关于如何使用库中的方法,请参考以下网站:
https://cryptojs.gitbook.io/docs/
以下页面显示了我的建议来自的示例:
描述
我希望在 Javascript 中使用 SHA256 在本地散列字符串。 我一直在四处寻找,认为会有某种官方图书馆或功能, 但我发现的是大量不同的项目,每个项目都有不同的脚本,而且我不太确定脚本是否值得信任(因为我不是专家,也绝对没有资格评估它们)或如何实施它们。 编辑:我需要文本输出,而不是十六进制,如果我在发布原始问题时没有解释,抱歉。
代码
这是我到目前为止尝试过的方法:
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);
控制台输出:
Uncaught (in promise) TypeError: Cannot read property 'digest' of undefined
我是 javascript 的新手,我愿意接受所有建议,所以是的。
更新
虽然您的大部分建议都有效,但对于那些希望使用 Web Crypto API 的人来说,答案在第 5 行。我需要将 crypto.subtle.digest
更改为 window.crypto.subtle.digest
检查这个:https://github.com/brix/crypto-js
您可以使用以下内容:
require(["crypto-js/aes", "crypto-js/sha256"], function (AES, SHA256)
{
console.log(SHA256("Message"));
});
或不要求:
<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>
你好 :D 这是一个很好的功能。 如果你是学者,你想看看这篇文章:https://www.movable-type.co.uk/scripts/sha256.html
纯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;
};
Cannot read property 'digest' of undefined
调用 crypto.subtle.digest
意味着 subtle
在 crypto
内不可用;因此,digest
不可能存在,因为它的包含模块不存在。
那么从逻辑上讲,crypto.subtle
一定不能在此范围内使用,事实上,在浏览器中也是如此 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.
在安全环境下,您的代码可以完美运行
1: 安全上下文 - Web security | MDN
2: 什么时候上下文被认为是安全的? - Secure contexts - Web security | MDN
3: Window.postMessage() - Web APIs | MDN
4: Window 功能特性 - Window.open() - Web APIs | MDN
代码没问题。 最后一行应如下所示:
var vDigest = await sha256(passwordInput);
上面来自 ofundefined 的答案有一堆缺少分号和错误。我已经清理了代码,因此您可以将其作为函数调用。我不确定这是否适用于 unicode 字符。可能必须将 unicode 转换为正常的 ascii 才能工作。但是,正如链接文章所说......这不应该在生产环境中使用。此外,此代码的原始版本似乎确实支持 unicode,因此使用它可能比使用此功能更好。
上面的文章(似乎支持 unicode)显示了这个函数,它与下面发布的函数不同...
https://www.movable-type.co.uk/scripts/sha256.html
如何使用
console.log(sha256('Perry Computer Services'));
输出
89bae4aeb761e42cb71ba6b62305e0980154cf21992c9ab2ab6fc40966ab5bf3
使用 PHP 哈希测试 - 输出
89bae4aeb761e42cb71ba6b62305e0980154cf21992c9ab2ab6fc40966ab5bf3
这不是上面其他链接页面上显示的完整功能。正如您从上面的示例中看到的那样,它确实可以“按原样”处理非 unicode 字符。
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;
};
2021 年更新 - SHA256 现在包含在当前浏览器中
正如您在问题中提到的,您不需要自定义加密实现来执行此操作。
WebCrypto is supported in all current browsers。使用 window.crypto.subtle.digest
生成 SHA 256 哈希值。
const digest = await window.crypto.subtle.digest('SHA-256', data);
如果你想要一些异步的东西,sha.js
例如每月有 1280 万次下载,并且正在积极维护。
const digest = shajs('sha256').update(data).digest('hex')
纯 javascript,不需要依赖项
您可以使用SubtleCrypto.digest()来帮助您。
它需要一个Uint8Array
如果您的数据是 Blob
const blob = new Blob([file])
const arrayBuffer = await blob.arrayBuffer()
const uint8Array = new Uint8Array(arrayBuffer)
SubtleCrypto.digest("SHA-256", uint8Array)
如果数据是字符串,使用TextEncoder.encode()转换为Uint8Array
const uint8Array = new TextEncoder().encode(data)
SubtleCrypto.digest("SHA-256", uint8Array)
下面是一个可运行的例子,供您参考。
<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>
另请参阅
您可以使用 CDN 库。
https://cdnjs.com/libraries/crypto-js
请将以下脚本标记引用到您的 html :
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js" integrity="sha512-E8QSvWZ0eCLGk4km3hxSsNmGWbLtSCSUcewDQPQWZF6pEU8GlT8a5fF32wOl1i8ftdMhssTrF/OhyGWwonTcXA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
关于如何使用库中的方法,请参考以下网站:
https://cryptojs.gitbook.io/docs/
以下页面显示了我的建议来自的示例: