zoukankan      html  css  js  c++  java
  • 在浏览器端如何进行文件签名的计算

    在浏览器端如何进行文件签名的计算

    现在有这样一个需求:我们需要对一个任意文件在浏览器端进行一个签名的计算,以供后一步的比对校验使用。

    要求:签名算法包括但不限于MD5、SHA-1、SHA-256、SHA-384、SHA-512等算法。

    经验:因为之前有简单调研过 Web Cryptography API 接口下的功能和底层实现,所以可以确保Web Cryptography拥有在计算签名方面的能力。

    思路:在 WebCryptoAPIREC推荐标准MDN Crypto接口接口支持情况查询后,初步判定我们可以使用Crypto API下的SubtleCrypto interface下的digest方法用于SHA系列算法的实现,MD5算法无法从Web Cryptography下获得需要另行找实现库或自己造轮子。

    接口支持情况

    SubtleCrypto interface的实现情况:

    Chrome Edge Firefox Internet Explorer Opera Safari WebView Android Chrome Android Firefox Android Opera Android iOS Safari Samsung Internet
    subtleExperimental Full support37 Full support12 Full support34Open Partial support11 Full support24 Full support10.1Open Full support37 Full support37 Full support34Open Full support24 Full support10.3Open

    Web Crypto接口下的算法及功能表,来自REC文档

    Algorithm name encrypt decrypt sign verify digest generateKey deriveKey deriveBits importKey exportKey wrapKey unwrapKey
    RSASSA-PKCS1-v1_5
    RSA-PSS
    RSA-OAEP
    ECDSA
    ECDH
    AES-CTR
    AES-CBC
    AES-GCM
    AES-KW
    HMAC
    SHA-1
    SHA-256
    SHA-384
    SHA-512
    HKDF
    PBKDF2

    我们的需求实现是在一个>=Gecko44.0版本的嵌入式设备上,满足我们需求的实现。

    原型实现

    我们需要使用digest方法,查看其方法描述:

    14.3.5. The digest method
    The digest method returns a new Promise object that will digest data using the specified AlgorithmIdentifier. It must act as follows:
    
    Let algorithm be the algorithm parameter passed to the digest method.
    
    Let data be the result of getting a copy of the bytes held by the data parameter passed to the digest method.
    
    Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set to algorithm and op set to "digest".
    
    If an error occurred, return a Promise rejected with normalizedAlgorithm.
    
    Let promise be a new Promise.
    
    Return promise and asynchronously perform the remaining steps.
    
    If the following steps or referenced procedures say to throw an error, reject promise with the returned error and then terminate the algorithm.
    
    Let result be the result of performing the digest operation specified by normalizedAlgorithm using algorithm, with data as message.
    
    Resolve promise with result.
    

    简单的说就是:输入生成摘要的算法类型数据,输出一个包含数据摘要Promise对象

    语法:

    const digest = crypto.subtle.digest(algorithm, data);
    

    简绘我们需要的流程图:

    文件 -> Uint8Array数据 -> 摘要算法 -> buffer摘要 -> 转Uint8Array并转成16进制串
    

    代码:

    var response = new Response(file);
    
    // 也可使用FileReader WebAPI或其他的BOM提供的处理文件的API
    response.arrayBuffer().then((arrayBuffer) => {
      
      // buffer -> uint8array
      var uint8Array = new Uint8Array(arrayBuffer);
      
      // digest()方法处理 -> 得到摘要buffer
      // type可选:SHA-1、SHA-256、SHA-384、SHA-512
      window.crypto.subtle.digest('SHA-512', uint8Array).then(hashBuffer => {
        
        // hashBuffer转普通数组
        var hashArray = Array.from(new Uint8Array(hashBuffer));
        
        // 摘要结果转16进制字符串表示
        // 其中padStart用于将字符'0-F'转为'00-0F',不然摘要长度将发生变化
        var hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
        
        console.log(hashHex);
      })
    });
    

    其中在我们的目标平台上需要兼容padStart方法,获取polyfill方式:

    // padStart()方法用另一个字符串填充当前字符串(如果需要的话,会重复多次),以便产生的字符串达到给定的长度。从当前字符串的左侧开始填充。
    // https://github.com/uxitten/polyfill/blob/master/string.polyfill.js
    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
    /**
     * String.padStart()
     * version 1.0.1
     * Feature	        Chrome  Firefox Internet Explorer   Opera	Safari	Edge
     * Basic support	57   	51      (No)	            44   	10      15
     * -------------------------------------------------------------------------------
     */
    if (!String.prototype.padStart) {
      String.prototype.padStart = function padStart(targetLength, padString) {
        targetLength = targetLength >> 0; //floor if number or convert non-number to 0;
        padString = String(typeof padString !== 'undefined' ? padString : ' ');
        if (this.length > targetLength) {
          return String(this);
        } else {
          targetLength = targetLength - this.length;
          if (targetLength > padString.length) {
            padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed
          }
          return padString.slice(0, targetLength) + String(this);
        }
      };
    }
    

    测试

    1. vim编辑文本文件test:'hello!' 并保存退出,使用sha512sum test命令计算得到:

    dd80bc1a06f03059682d2bb1327a0c3b3eee84c46fe7d973e804fe76d433738209ecc8bc49f8bae345fef1eb865dbc92f17fd83ac584f9760cf62bd92af5075c

    1. 在浏览器端敲入以下内容,因为编辑器vim自动添加了换行符,故此处需补换行符
    var response = new Response(new Blob(['hello!
    ']));
    response.arrayBuffer().then((arrayBuffer) => {
      var uint8Array = new Uint8Array(arrayBuffer);
      window.crypto.subtle.digest('SHA-512', uint8Array).then(hashBuffer => {
        var hashArray = Array.from(new Uint8Array(hashBuffer));
        var hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
        console.log(hashHex);
      })
    });
    //  dd80bc1a06f03059682d2bb1327a0c3b3eee84c46fe7d973e804fe76d433738209ecc8bc49f8bae345fef1eb865dbc92f17fd83ac584f9760cf62bd92af5075c
    

    步骤1,2结果呈一致性。也可实际在浏览器端使用input输入文件或者输入服务器端的文件进行测试。

    扩展-MD5

    Github上基于MD5摘要算法实现的库有非常多,可轻松进行扩展。笔者目前所使用的 browser-md5-file

    它的API描述:

    browserMD5File(file, (err, md5) => {})
    

    注意

    因为MD5摘要算法为上层(JS库)实现,所以速度是没有SHA-系列摘要算法(底层使用的C实现的OpenSSL)速度快的。并且由于MD5的可碰撞性,不推荐作为目前的日常使用。

    同理,使用Web Crypto下的API去进行对文件的加解密工作、签名和验证签名工作都是可以实现的。

  • 相关阅读:
    【git】------git开发过程中的使用流程------
    js原型链的深度理解!
    mvc框架模式
    node环境下express路由,
    前后端 分离及不分离
    node中间件概念
    JS-------DOM0级事件处理和DOM2级事件处理-------简单记法
    call、apply、bind的区别
    移动端Click300毫秒点击延迟
    JS 详解 Cookie、 LocalStorage 与 SessionStorage
  • 原文地址:https://www.cnblogs.com/hencins/p/14292900.html
Copyright © 2011-2022 走看看