在javascript中从Blob / File创建SHA-256哈希

时间:2014-02-13 17:36:06

标签: javascript html5 performance hash sha

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

var reader = new FileReader();
reader.onload = function() {
    // this gets read 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秒。我怎样才能改善这个?

5 个答案:

答案 0 :(得分:16)

您可能需要查看Stanford JS加密库

GitHub

Website with Examples

来自网站:

  

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

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

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

测试page

示例代码:

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

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

答案 1 :(得分:6)

这是一个老问题,但我认为值得注意的是asmCrypto明显快于jsSHA,并且快于CryptoJSSJCL

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

还有一个由OpenPGP.js

维护的精简版(上面的一个分支)

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

其中仅包含SHA256和几个AES功能。

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

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

我可以在&lt; 中散列 150MB + 文件Chrome中一致<2秒

答案 2 :(得分:4)

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

  

Q值。编译代码的速度有多快?

     

一个。 Emscripten的默认代码生成模式为asm.js格式,   这是JavaScript的一个子集,旨在使其成为可能   JavaScript引擎可以非常快速地执行。请看这里是最新的   基准结果。在许多情况下,asm.js可以非常接近原生   速度。

您可以找到Emscripten编译的NaCl加密库here

答案 3 :(得分:3)

这是你要找的东西。我从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;



};

答案 4 :(得分:0)

我使用 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>