繁体   English   中英

从 javascript 中的 Blob/文件创建 SHA-256 hash

[英]Create SHA-256 hash from a Blob/File in javascript

我需要从浏览器内的文件 (~6MB) 创建 SHA-256 摘要。 到目前为止,我设法做到的唯一方法是这样的:

var reader = new FileReader();
reader.onload = function() {
    // this gets rid of the mime-type data header
    var actual_contents = reader.result.slice(reader.result.indexOf(',') + 1);
    var what_i_need = new jsSHA(actual_contents, "B64").getHash("SHA-256", "HEX");
}
reader.readAsDataURL(some_file);

虽然这可以正常工作,但问题是它非常慢。 一个 6MB 的文件大约需要 2-3 秒。 我该如何改进呢?

你可能想看看斯坦福 JS 加密库

GitHub

带有示例的网站

从网站:

SJCL 是安全的。 它使用 128、192 或 256 位的行业标准 AES 算法; SHA256 哈希函数; HMAC认证码; PBKDF2 密码强化程序; 以及 CCM 和 OCB 认证加密模式。

SJCL 有一个测试页面,显示需要多长时间。

SHA256 迭代需要 184 毫秒。 来自 catameringue 的 SHA-256 为 50 毫秒。

测试

示例代码:

加密数据: sjcl.encrypt("password", "data")

解密数据: sjcl.decrypt("password", "encrypted-data")

这是一个老问题,但我认为值得注意的是asmCryptojsSHA ,并且比CryptoJSSJCL

https://github.com/vibornoff/asmcrypto.js/

还有一个由OpenPGP.js维护的精简版(上述的一个分支)

https://github.com/openpgpjs/asmcrypto-lite

其中仅包括 SHA256 和一些 AES 功能。

要使用asmCrypto您只需执行以下操作:

var sha256HexValue = asmCrypto.SHA256.hex(myArraybuffer);

我能够在 Chrome 中始终如一地在< 2 秒内散列一个150MB+ 的文件。

使用加密库的emscripten编译版本可能会更快,

问:编译后的代码有多快?

A. Emscripten 的默认代码生成模式是 asm.js 格式,它是 JavaScript 的一个子集,旨在使 JavaScript 引擎能够非常快速地执行。 请参阅此处了解最新的基准测试结果。 在许多情况下,asm.js 可以非常接近原生速度。

您可以在此处找到 Emscripten 编译的 NaCl 加密库。

这就是您要找的东西。 我从 SHA256 算法的 C 版本推导出来。 它还包括 SHA256D。 我认为使用 javascript 不会比这快得多。 我尝试扩展循环,但由于 javascript 解释器运行的优化,它运行速度较慢。

// From: https://github.com/Hartland/GPL-CPU-Miner/blob/master/sha2.c

if ("undefined" == typeof vnet) {
    vnet = new Array();
}

if ("undefined" == typeof vnet.crypt) {
    vnet.crypt = new Array();
}

vnet.crypt.sha2 = function() {

    var sha256_h = [
        0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
        0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
    ];

    var sha256_k = [
                    0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
                    0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
                    0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
                    0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
                    0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
                    0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
                    0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
                    0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
                    0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
                    0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
                    0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
                    0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
                    0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
                    0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
                    0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
                    0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
    ];

    var sha256_init = function(s) {
        s.state = [
                   sha256_h[0],
                   sha256_h[1],
                   sha256_h[2],
                   sha256_h[3],
                   sha256_h[4],
                   sha256_h[5],
                   sha256_h[6],
                   sha256_h[7],
        ];
    }; this.sha256_init = sha256_init;

/*
* SHA256 block compression function. The 256-bit state is transformed via
* the 512-bit input block to produce a new state.
*/
    var sha256_transform = function(s, b, swap) {

        var block = b.block;
        var state = s.state;

        var W;
        var S;
        var t0;
        var t1;
        var i;

        /* 1. Prepare message schedule W. */
        if (swap) {


            W = [
                 ((((block[0] ) << 24) & 0xff000000) | (((block[0] ) << 8) & 0x00ff0000) | (((block[0] ) >> 8) & 0x0000ff00) | (((block[0] ) >> 24) & 0x000000ff)),
                 ((((block[1] ) << 24) & 0xff000000) | (((block[1] ) << 8) & 0x00ff0000) | (((block[1] ) >> 8) & 0x0000ff00) | (((block[1] ) >> 24) & 0x000000ff)),
                 ((((block[2] ) << 24) & 0xff000000) | (((block[2] ) << 8) & 0x00ff0000) | (((block[2] ) >> 8) & 0x0000ff00) | (((block[2] ) >> 24) & 0x000000ff)),
                 ((((block[3] ) << 24) & 0xff000000) | (((block[3] ) << 8) & 0x00ff0000) | (((block[3] ) >> 8) & 0x0000ff00) | (((block[3] ) >> 24) & 0x000000ff)),
                 ((((block[4] ) << 24) & 0xff000000) | (((block[4] ) << 8) & 0x00ff0000) | (((block[4] ) >> 8) & 0x0000ff00) | (((block[4] ) >> 24) & 0x000000ff)),
                 ((((block[5] ) << 24) & 0xff000000) | (((block[5] ) << 8) & 0x00ff0000) | (((block[5] ) >> 8) & 0x0000ff00) | (((block[5] ) >> 24) & 0x000000ff)),
                 ((((block[6] ) << 24) & 0xff000000) | (((block[6] ) << 8) & 0x00ff0000) | (((block[6] ) >> 8) & 0x0000ff00) | (((block[6] ) >> 24) & 0x000000ff)),
                 ((((block[7] ) << 24) & 0xff000000) | (((block[7] ) << 8) & 0x00ff0000) | (((block[7] ) >> 8) & 0x0000ff00) | (((block[7] ) >> 24) & 0x000000ff)),
                 ((((block[8] ) << 24) & 0xff000000) | (((block[8] ) << 8) & 0x00ff0000) | (((block[8] ) >> 8) & 0x0000ff00) | (((block[8] ) >> 24) & 0x000000ff)),
                 ((((block[9] ) << 24) & 0xff000000) | (((block[9] ) << 8) & 0x00ff0000) | (((block[9] ) >> 8) & 0x0000ff00) | (((block[9] ) >> 24) & 0x000000ff)),
                 ((((block[10]) << 24) & 0xff000000) | (((block[10]) << 8) & 0x00ff0000) | (((block[10]) >> 8) & 0x0000ff00) | (((block[10]) >> 24) & 0x000000ff)),
                 ((((block[11]) << 24) & 0xff000000) | (((block[11]) << 8) & 0x00ff0000) | (((block[11]) >> 8) & 0x0000ff00) | (((block[11]) >> 24) & 0x000000ff)),
                 ((((block[12]) << 24) & 0xff000000) | (((block[12]) << 8) & 0x00ff0000) | (((block[12]) >> 8) & 0x0000ff00) | (((block[12]) >> 24) & 0x000000ff)),
                 ((((block[13]) << 24) & 0xff000000) | (((block[13]) << 8) & 0x00ff0000) | (((block[13]) >> 8) & 0x0000ff00) | (((block[13]) >> 24) & 0x000000ff)),
                 ((((block[14]) << 24) & 0xff000000) | (((block[14]) << 8) & 0x00ff0000) | (((block[14]) >> 8) & 0x0000ff00) | (((block[14]) >> 24) & 0x000000ff)),
                 ((((block[15]) << 24) & 0xff000000) | (((block[15]) << 8) & 0x00ff0000) | (((block[15]) >> 8) & 0x0000ff00) | (((block[15]) >> 24) & 0x000000ff))
            ];
        } else {
            W = [
                 block[0],
                 block[1],
                 block[2],
                 block[3],
                 block[4],
                 block[5],
                 block[6],
                 block[7],
                 block[8],
                 block[9],
                 block[10],
                 block[11],
                 block[12],
                 block[13],
                 block[14],
                 block[15]
            ];
        }


        for (i = 16; i < 64; i += 2) {
            W[i] = ((
                ((((W[i-2] >>> 17) | (W[i-2] << 15)) ^ ((W[i-2] >>> 19) | ((W[i-2] << 13)>>>0) ) ^ (W[i - 2] >>> 10)) >>> 0) + //s1 (W[i - 2]) + 
                W[i - 7] + 
                ((((W[i - 15] >>> 7) | (W[i - 15] << 25)) ^ ((W[i - 15] >>> 18) | ((W[i - 15] << 14) >>> 0)) ^ (W[i - 15] >>> 3))  >>> 0) + //s0 (W[i - 15]) + 
                W[i - 16]
            ) & 0xffffffff) >>> 0;

            W[i+1] = ((
                ((((W[i-1] >>> 17) | (W[i-1] << 15)) ^ ((W[i-1] >>> 19) | (W[i-1] << 13)) ^ (W[i - 1] >>> 10)) >>> 0)+ //s1 (W[i - 1]) + 
                W[i - 6] + 
                ((((W[i - 14] >>> 7) | (W[i - 14] << 25)) ^ ((W[i - 14] >>> 18) | (W[i - 14] << 14)) ^ (W[i - 14] >>> 3)) >>> 0)  + //s0 (W[i - 14]) + 
                W[i - 15]
            ) & 0xffffffff) >>> 0;
        }


        /* 2. Initialize working variables. */

        S = [
         state[0],
         state[1],
         state[2],
         state[3],
         state[4],
         state[5],
         state[6],
         state[7],
        ];

        /* 3. Mix. */


        i=0;
        for(;i<64;++i) {

            //RNDr(S,W,i)
            t0 = S[(71 - i) % 8] + 
                ((((S[(68 - i) % 8] >>> 6) | (S[(68 - i) % 8]  << 26)) ^ ((S[(68 - i) % 8] >>> 11) | (S[(68 - i) % 8] << 21)) ^ ((S[(68 - i) % 8] >>> 25) | (S[(68 - i) % 8] << 7)))) + //S1 (S[(68 - i) % 8]) +
                (((S[(68 - i) % 8] & (S[(69 - i) % 8] ^ S[(70 - i) % 8])) ^ S[(70 - i) % 8]) ) + // Ch
                W[i] + 
                sha256_k[i];

            t1 = ((((S[(64 - i) % 8] >>> 2) | ((S[(64 - i) % 8] & 3) << 30)) ^ ((S[(64 - i) % 8] >>> 13) | (S[(64 - i) % 8] << 19)) ^ ((S[(64 - i) % 8] >>> 22) | (S[(64 - i) % 8] << 10)))) + //S0 (S[(64 - i) % 8]) +
                (((S[(64 - i) % 8] & (S[(65 - i) % 8] | S[(66 - i) % 8])) | (S[(65 - i) % 8] & S[(66 - i) % 8]))); // Maj

            S[(67 - i) % 8] = ((S[(67 - i) % 8] + t0) & 0xFFFFFFFF) >>> 0; 
            S[(71 - i) % 8] = ((t0 + t1) & 0xFFFFFFFF) >>> 0;
        }

        /* 4. Mix local working variables into global state */

        i=0;
        for(;i<8;++i) {
            s.state[i] = (0xFFFFFFFF & (state[i] + S[i])) >>> 0;
        }

    }; this.sha256_transform = sha256_transform;

    var sha256d_hash1 = [
        0x00000000, 0x00000000, 0x00000000, 0x00000000,
        0x00000000, 0x00000000, 0x00000000, 0x00000000,
        0x80000000, 0x00000000, 0x00000000, 0x00000000,
        0x00000000, 0x00000000, 0x00000000, 0x00000100
    ];

    var sha256d_80_swap = function(hash, data) 
    {

        var S = new Array();

        var i;

        var b1 = new Array();
        var b2 = new Array();
        var b3 = new Array();

        b1.block = [
            data[0],
            data[1],
            data[2],
            data[3],
            data[4],
            data[5],
            data[6],
            data[7],
            data[8],
            data[9],
            data[10],
            data[11],
            data[12],
            data[13],
            data[14],
            data[15]
        ];

        b2.block = [
            data[16],
            data[17],
            data[18],
            data[19],
            data[20],
            data[21],
            data[22],
            data[23],
            data[24],
            data[25],
            data[26],
            data[27],
            data[28],
            data[29],
            data[30],
            data[31]
        ];

        sha256_init(S);
        sha256_transform(S, b1, 0);
        sha256_transform(S, b2, 0);

        b3.block = [
            S.state[0],
            S.state[1],
            S.state[2],
            S.state[3],
            S.state[4],
            S.state[5],
            S.state[6],
            S.state[7],
            sha256d_hash1[8],
            sha256d_hash1[9],
            sha256d_hash1[10],
            sha256d_hash1[11],
            sha256d_hash1[12],
            sha256d_hash1[13],
            sha256d_hash1[14],
            sha256d_hash1[15]
        ];

        sha256_init(hash);
        sha256_transform(hash, b3, 0);

        for (i = 0; i < 8; i++) {
            hash.state[i] = ((((hash.state[i] ) << 24) & 0xff000000) | (((hash.state[i] ) << 8) & 0x00ff0000) | (((hash.state[i] ) >> 8) & 0x0000ff00) | (((hash.state[i] ) >> 24) & 0x000000ff)); //swab32(hash[i]);
        }

    }; this.sha256d_80_swap = sha256d_80_swap;

    var sha256d = function(hash, data) {
        var S;
        var T;
        var block_in;

        S = new Array();
        T = new Array();

        T.block = [];

        var i, r;

        //hash.hash = new Array(32).join('0').split('').map(parseFloat);

        sha256_init(S);

        for (r = data.length; r > -9; r -= 64) {
            if (r < 64) {
                if (r > 0) {
                    block_in = data.slice(data.length - r,data.length);
                    block_in.push.apply(block_in, new Array(64-r).join('0').split('').map(parseFloat));
                } else {
                    block_in = new Array(64).join('0').split('').map(parseFloat);
                }
            } else {
                block_in = data.slice(data.length - r,data.length - r + 64);
            }

            //memcpy(T, data + len - r, r > 64 ? 64 : (r < 0 ? 0 : r));

            if (r >= 0 && r < 64) {
                block_in[r] = 0x80;
            } 

            for (i = 0; i < 16; i++) {
                T.block[i] = (((0xff & block_in[(i*4)]) << 24) | ((0xff & block_in[(i*4)+1]) << 16) | ((0xff & block_in[(i*4)+2]) << 8) | (0xff & block_in[(i*4)+3])) >>> 0;
            }

            if (r < 56) {
                T.block[15] = 8 * data.length;
            }

            sha256_transform(S, T, 0);
        }
        //memcpy(S + 8, sha256d_hash1 + 8, 32);
        S.block = S.state;
        for(i=8;i<16;i++) {
            S.block[i] =  sha256d_hash1[i];
        }

        sha256_init(T);
        sha256_transform(T, S, 0);

        hash.hash = [ 
                  (T.state[0] >> 24) & 0xff,
                  (T.state[0] >> 16) & 0xff,
                  (T.state[0] >> 8) & 0xff,
                  T.state[0] & 0xff,

                  (T.state[1] >> 24) & 0xff,
                  (T.state[1] >> 16) & 0xff,
                  (T.state[1] >> 8) & 0xff,
                  T.state[1] & 0xff,

                  (T.state[2] >> 24) & 0xff,
                  (T.state[2] >> 16) & 0xff,
                  (T.state[2] >> 8) & 0xff,
                  T.state[2] & 0xff,

                  (T.state[3] >> 24) & 0xff,
                  (T.state[3] >> 16) & 0xff,
                  (T.state[3] >> 8) & 0xff,
                  T.state[3] & 0xff,

                  (T.state[4] >> 24) & 0xff,
                  (T.state[4] >> 16) & 0xff,
                  (T.state[4] >> 8) & 0xff,
                  T.state[4] & 0xff,

                  (T.state[5] >> 24) & 0xff,
                  (T.state[5] >> 16) & 0xff,
                  (T.state[5] >> 8) & 0xff,
                  T.state[5] & 0xff,

                  (T.state[6] >> 24) & 0xff,
                  (T.state[6] >> 16) & 0xff,
                  (T.state[6] >> 8) & 0xff,
                  T.state[6] & 0xff,

                  (T.state[7] >> 24) & 0xff,
                  (T.state[7] >> 16) & 0xff,
                  (T.state[7] >> 8) & 0xff,
                  T.state[7] & 0xff
         ];

    }; this.sha256d = sha256d;



    var sha256 = function(hash, data) {
        var S;
        var T;
        var block_in;

        S = new Array();
        T = new Array();

        T.block = [];

        var i, r;

        hash.hash = new Array(32).join('0').split('').map(parseFloat);

        sha256_init(S);

        for (r = data.length; r > -9; r -= 64) {

            if (r < 64) {
                if (r > 0) {
                    block_in = data.slice(data.length - r,data.length);
                    block_in.push.apply(block_in, new Array(64-r).join('0').split('').map(parseFloat));
                } else {
                    block_in = new Array(64).join('0').split('').map(parseFloat);
                }
            } else {
                block_in = data.slice(data.length - r,data.length - r + 64);
            }

            //memcpy(T, data + len - r, r > 64 ? 64 : (r < 0 ? 0 : r));

            if (r >= 0 && r < 64) {
                block_in[r] = 0x80;
            } 

            for (i = 0; i < 16; i++) {
                T.block[i] = (((0xff & block_in[(i*4)]) << 24) | ((0xff & block_in[(i*4)+1]) << 16) | ((0xff & block_in[(i*4)+2]) << 8) | (0xff & block_in[(i*4)+3])) >>> 0;
            }

            if (r < 56) {
                T.block[15] = 8 * data.length;
            }

            sha256_transform(S, T, 0);
        }

        for (i = 0; i < 8; i++) {
            //be32enc((uint32_t *)hash + i, T[i]);
            hash.hash[(i * 4)] = (S.state[i] >> 24) & 0xff;
            hash.hash[(i * 4)+1] = (S.state[i] >> 16) & 0xff
            hash.hash[(i * 4)+2] = (S.state[i] >> 8) & 0xff
            hash.hash[(i * 4)+3] = S.state[i] & 0xff;
        }
    }; this.sha256 = sha256;



};

正如一些人回答的那样,它可以在 vanillajs 中完成:

async function getChecksumSha256(blob: Blob): Promise<string> {
  const uint8Array = new Uint8Array(await blob.arrayBuffer());
  const hashBuffer = await crypto.subtle.digest('SHA-256', uint8Array);
  const hashArray = Array.from(new Uint8Array(hashBuffer));

  return hashArray.map((h) => h.toString(16).padStart(2, '0')).join('');
}

资料来源:https://gist.github.com/bilelz/c96fb0b1f62983d061910e8d310a5162

你可以在没有外部库的情况下使用Crypto.subtle API 做到这一点。 更多细节 在这里

例子:

function b2h(buffer) {
    return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');
}

const FILEREADER = new FileReader();
FILEREADER.readAsArrayBuffer(file);
FILEREADER.onloadend = async function(entry) {
    const FILE_HASH = b2h(await crypto.subtle.digest('SHA-256', entry.target.result)); // output: the sha256 digest hex encoded of the file
}

我使用SubtleCrypto.digest()

测试文件大约 85MB,不需要一秒钟就可以完成。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM