web cryptography API


web cryptography API

論文原鏈接:https://www.theseus.fi/bitstream/handle/10024/92960/Web_Cryptography_API_Luoma-aho.pdf

背景

  • 隨着數據安全的需求提高,另外js的流行,出現越來越多的前端富應用。用戶對客戶端加密/前端(瀏覽器端)加密的需求越來越強。
  • 在已經有https(ssl/tsl)傳輸的基礎上,前端的加解密是否必要?--本文探討的重點
  • js的加解密有很多弊端,比如:
    • 缺少密碼學安全的隨機數生成器(CSPRNG)
    • 缺少安全的私鑰存儲機制
    • 不可預估的計算性能瓶頸
  • 一些流行的js加解密庫:
    Stanford JavaScript Crypto Library(sjcl)
    CryptoJS
    Forge

Web Cryptography API

crypto知識簡單介紹

  • 公鑰加解密(非對稱加解密)
  • 對稱加解密
  • 數字簽名,提供身份驗證和信息完整驗證(未被篡改)
    簽名的key和加解密的key不能共用,簽名具有法律效益。Web Cryptography API也對此做了限制。你不能生成同時用來加密和簽名的key
  • 信息摘要(Message Digest),摘要算法是單向的(你不能恢復出原文),如MD5,SHA-1,SHA-256,SHA-512。MD5和sha-1有碰撞風險,不建議繼續使用。主要用途:
    • 信息完整驗證(未被篡改)
    • 創建唯一的標識符

基本使用

  • window.crypto.subtle就是SubtleCrypto接口
  • 主流最新瀏覽器基本都已經支持該接口,並且一致,除了safari,需要注意
// fix safari crypto namespace
 if (window.crypto && !window.crypto.subtle && window.crypto.webkitSubtle) {
    window.crypto.subtle = window.crypto.webkitSubtle; 
 }

 /**
* Detect Web Cryptography API
* @return {Boolean} true, if success 
*/
function isWebCryptoAPISupported() {
    return 'crypto' in window && 'subtle' in window.crypto;
}
  • getRandomValues 同步方法,獲取隨機數,因為性能要求,這是一個偽隨機數生成器(PRNG)。瀏覽器也通過添加系統級別的種子來提高熵(不確定性的量度),來滿足密碼學使用要求。
 var size = 10;
 var array = new Uint8Array(size);
 window.crypto.getRandomValues(array);

 // print values to console
 for (var i=0; i!==array.length; ++i) {
   console.log(array[i]);
 }
  • SubtleCrypto接口

    • encrypt,decrypt(加解密方法),算法支持:
      • RSA-OAEP
      • AES-CTR
      • AES-CBC
      • AES-GCM
      • AES-CFB
    • Sign,verify(簽名驗簽發方法),算法支持:
      • RSASSA-PKCS1-v1.5
      • RSA-PSS
      • ECDSA
      • AES-CMAC
      • HMAC
    • Digest(摘要方法),算法支持:
      • SHA-1, SHA-256, SHA-384, SHA-512
    • GenerateKey
      生成對稱/非對稱的密鑰(CryptoKey對象)
    • DeriveKey 和 deriveBits
      從原密鑰或是從偽隨機函數生成的密碼/口令中生成出一個密鑰
    • WrapKey 和 unwrapKey
      用來保護在不安全信道或不可信環境存儲的密鑰的方法
    • ImportKey 和 exportKey
      導入密鑰,轉換再導出不同格式

    上面的方法都會返回Promise

 var message = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
    var cryptoKeyPair; // for storing the CryptoKey object
    var ciphertext; // for storing the encryption result
    var plaintext; // for storing the decryption result
    var algorithm = {
        name: "RSA-OAEP",
        modulusLength: 2048, // 1024, 2048, 4096
        publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
        hash: {
            name: "SHA-256" // SHA-1, SHA-256, SHA-384, SHA-512
        }
    };

    // Generate keys
    window.crypto.subtle.generateKey(algorithm,
        false, // non-exportable 
        ["encrypt", "decrypt"] // usage
    )
        .then(function (result) {
            // Store keys
            cryptoKeyPair = result;
        })
        .then(function () {
            // Encrypt
            var data = utils.convertTextToUint8Array(message);
            return window.crypto.subtle.encrypt(algorithm, cryptoKeyPair.publicKey, data);
        })
        .then(function (result) {
            // Store ciphertext
            ciphertext = new Uint8Array(result);
            // console.log('ciphertext', ciphertext);
        })
        .then(function () {
            // Output 
            console.log('Encrypted data:');
            console.log(utils.convertUint8ArrayToHexView(ciphertext, 16, ''));
        })
        .then(function () {
            // Decrypt
            return window.crypto.subtle.decrypt(
                algorithm, cryptoKeyPair.privateKey, ciphertext
            );
        })
        .then(function (result) {
            // Store plaintext
            plaintext = new Uint8Array(result);
        })
        .then(function () {
            // Output
            console.log('Decrypted data:');
            console.log(utils.convertUint8ArrayToText(plaintext));
        })
  • 導出jwk(JSON Web Key)格式
 var algorithm = {
        name: "RSA-OAEP",
        modulusLength: 2048, // 1024, 2048, 4096 
        publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
        hash: {
            name: "SHA-256" // "SHA-1", "SHA-256", "SHA-384", "SHA -512"
        }
    }
    window.crypto.subtle.generateKey(algorithm,
        false, // non-exportable 
        ["encrypt", "decrypt"] // usage
    )
        .then(function (cryptoKey) {
            return window.crypto.subtle.exportKey(
                'jwk', // export format 
                cryptoKey.publicKey
            );
        })
        .then(function (exportedKey) {
            console.log('Exported key:');
            console.log(JSON.stringify(exportedKey));
        });
  • CryptoKey對象可以存儲在indexdb里(支持結構化克隆算法),不適合存儲在localStorage里(只能存儲簡單對象)

  • SubtleCrypto,瀏覽器中的支持情況如下圖

開發者需要熟知瀏覽器環境的安全常識:

  • HTTP Strict Transport Security standard (HSTS)

規則讓瀏覽器知道網站只能通過http安全連接來訪問

  • Content Security Policy (CSP)

規則通知瀏覽器應該從何處加載資源

  • HTTP Public Key Pinning (HPKP)

它允許web主機運營商指導用戶代理在一段時間內強制使用特定的加密身份,從而有效地減輕某些中間人攻擊

  • Subresource integrity (SRI)

它允許用戶代理在不進行意外操作的情況下驗證獲取的資源是否已被傳遞。

開發者需要了解如下警告:

  • 標注了'encrypt'使用屬性的CryptoKey不能用來解密,反之亦然,標注了'decrypt'的不能用來加密。因為通常用於執行數據的數字簽名。
    而在使用API做數字簽名時,必須使用一對帶簽名和驗簽功能的鑰匙。

  • 非對稱加密算法不是用來加密長消息的。通常消息的長度要小於密鑰長度(比如一個2048位的RSA-OAEP算法可以加密最長1704位(或213字節)的消息,因為為了安全原因,還會加上一些字節填充)

  • 密鑰存儲在瀏覽器中時,攻擊者可能利用系統漏洞,采用xss等攻擊手段來盜取密鑰。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM