javascript 如何在JS中用SHA256散列一个字符串?

xzabzqsa  于 2022-10-30  发布在  Java
关注(0)|答案(9)|浏览(192)

描述

我想在Javascript中用SHA256在本地散列一个字符串。我一直在四处寻找,以为会有某种官方库或函数,但我发现的都是不同项目的负载,每个项目都有不同的脚本,我不太确定脚本是否值得信任(因为我不是Maven,肯定没有资格评估它们),也不知道如何实现它们。**编辑:**我需要文本输出,而不是hexes,抱歉,如果我在发布原始问题时没有解释这一点。

代码

以下是我目前所做的尝试:

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);

控制台输出:

未捕获(在承诺中)TypeError:无法读取未定义的属性'digest'
我是javascript的新手,我愿意接受所有的建议,所以是的。

更新

虽然您的大多数建议都有效,但对于那些希望使用Web Crypto API的人,答案在第5行。我需要将crypto.subtle.digest更改为window.crypto.subtle.digest

mrzz3bfm

mrzz3bfm1#

如果你是一个学者,你会想看看这篇文章: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;
};

来源:https://geraintluff.github.io/sha256/

d4so4syb

d4so4syb2#

2021更新-SHA 256现在包含在当前浏览器中

正如您在问题中提到的,您不需要自定义Crypto实现来实现这一点。
WebCrypto is supported in all current browsers。使用window.crypto.subtle.digest生成SHA 256哈希。

const textAsBuffer = new TextEncoder().encode(text);
const hashBuffer = await window.crypto.subtle.digest('SHA-256', textAsBuffer);
const hashArray = Array.from(new Uint8Array(hashBuffer))
const digest = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');

如果你想要同步的东西,例如sha.js每月有1280万次下载,并且是积极维护的。

const digest = shajs('sha256').update(data).digest('hex')
ef1yzkbh

ef1yzkbh3#

请查看: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>
2vuwiymt

2vuwiymt4#

  • 纯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>
7z5jn7bk

7z5jn7bk5#

Cannot read property 'digest' of undefined当调用crypto.subtle.digest时意味着subtlecrypto内不可用;因此digest不可能存在,因为它的包含模块不存在。
从逻辑上讲,crypto.subtle在此范围内一定不可用,实际上,在浏览器**anywhere outside of a secure context中也是如此。**

什么情况下上下文是安全的?-developer.mozilla.org

当上下文被安全地(或本地地)传递,并且当它不能被用来向不安全的上下文提供对安全API的访问时,上下文将被认为是安全的。实际上,这意味着对于具有安全上下文的页面,它和沿着其父级和打开器链的所有页面必须已经被安全地传递。
例如,如果通过TLS安全传递的页面具有未被安全传递的父文档或祖先文档,则该页面不被视为安全上下文;类似地,如果TLS传递的文档由不安全的上下文在新窗口中打开,而没有指定noopener,则打开的窗口不被认为是安全上下文(因为opener和打开的窗口可以通过postMessage通信)。
本地传送的文件(如http://localhost* 和 file:// 路径)被视为已安全传送。

非本地的上下文必须通过 https://wss:// 提供服务,并且其中使用的协议不应被视为过时。

在安全的上下文中,您的代码可以完美地工作😄

**1:**安全上下文-Web security | MDN
**2:**什么情况下上下文被认为是安全的?-安全上下文- Web安全|MDN网络
**3:**窗口.postMessage()-Web APIs | MDN
**4:**窗口功能特性-Window.open()- Web API| MDN网络

gstyhher

gstyhher6#

代码很好。最后一行应该看起来像这样:

var vDigest = await sha256(passwordInput);
zpgglvta

zpgglvta7#

上面的ofundefined的答案有很多遗漏的分号和错误。我已经清理了代码,所以你可以把它作为一个函数调用。我不确定它是否能和unicode字符一起工作。可能需要把unicode转换成普通的ascii才能工作。但是,正如链接的文章所说.... a.这不应该在生产环境中使用。另外,看起来这个代码的原始版本确实支持unicode,所以也许使用unicode比使用这个函数更好。
上面的文章(似乎支持unicode)显示了这个函数,它与下面发布的函数不同...
https://www.movable-type.co.uk/scripts/sha256.html

使用方法

console.log(sha256('Perry Computer Services'));

输出

89bae4aeb761e42cb71ba6b62305e0980154cf21992c9ab2ab6fc40966ab5bf3

已使用PHP哈希测试-输出

89bae4aeb761e42cb71ba6b62305e0980154cf21992c9ab2ab6fc40966ab5bf3

这不是上面链接页面中的FULL函数,它可以“原样”处理非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;
};
fkvaft9z

fkvaft9z8#

另请参考如何在浏览器JavaScript中计算字符串的SHA散列
您可以使用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/
下一页显示了我的建议来源的一个示例:
https://codepen.io/omararcus/pen/QWwBdmo

mm9b1k5b

mm9b1k5b9#

快回答:

const sha256 = async (data) => {
    const textAsBuffer = new TextEncoder().encode(data);
    const hashBuffer = await window.crypto.subtle.digest('SHA-256', textAsBuffer);
    const hashArray = Array.from(new Uint8Array(hashBuffer))
    const digest = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
    }
sha256("data")

相关问题