1. 场景

浏览器原生支持了AES-GCM。

参考资料:https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/encrypt

 

2. 代码

/**
 * AES-GCM helper
 */
var aesGcmHelper = {
  _keyMap: new Map(),

  getIv(keyRaw) {
    if (this._keyMap.has(keyRaw)) {
      return this._keyMap.get(keyRaw).iv;
    }
    let iv = crypto.getRandomValues(new Uint8Array(12));
    this._keyMap.set(keyRaw, Object.assign({}, this._keyMap.get(keyRaw), { iv }));
    return iv;
  },

  /**
   * hex to ArrayBuffer
   * @param {String} hex
   */
  hex2Buf(hex) {
    let buf = new Uint8Array(
      hex.match(/[\da-f]{2}/gi).map(function (h) {
        return parseInt(h, 16);
      })
    );
    return buf;
  },

  /**
   * base64 to ArrayBuffer
   * @param {String} hex
   */
  base64ToBuf(base64) {
    var binaryString = window.atob(base64);
    var bytes = new Uint8Array(binaryString.length);
    for (var i = 0; i < binaryString.length; i++) {
      bytes[i] = binaryString.charCodeAt(i);
    }
    return bytes.buffer;
  },
  /** * ArrayBuffer to hex * @param {ArrayBuffer} buffer */ buf2Hex(buffer) {
    let binary = [...new Uint8Array(buffer)].map((x) => x.toString(16).padStart(2, '0')).join('');
    return binary;
  },

  /**
   * ArrayBuffer to base64
   * @param {ArrayBuffer} buffer
   */
  buf2Base64(buffer) {
    let binary = [...new Uint8Array(buffer)].map((x) => String.fromCharCode(x)).join('');
    return window.btoa(binary);
  },

  /**
   * encrypt
   * @param {String} data data
   * @param {String} keyRaw key raw
   * @param {Uint8Array} iv iv
   * @return {Promise}
   */
  async encrypt(data, keyRaw, iv = null) {
    if (iv == null) {
      iv = this.getIv(keyRaw);
    } else {
      this._keyMap.set(keyRaw, Object.assign({}, this._keyMap.get(keyRaw), { iv }));
    }

    let encoded = new TextEncoder().encode(data);
    let key = await crypto.subtle.importKey('raw', new TextEncoder().encode(keyRaw), 'AES-GCM', true, ['encrypt', 'decrypt']);
    let ciphertext = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, encoded);
    return ciphertext;
  },

  /**
   * decrypt
   * @param {ArrayBuffer} encrypted encrypted data
   * @param {String} keyRaw key raw
   * @param {Uint8Array} iv iv
   * @return {Promise}
   */
  async decrypt(encrypted, keyRaw, iv = null) {
    if (iv == null) {
      iv = this.getIv(keyRaw);
    }
    if (!ArrayBuffer.isView(encrypted) && toString.apply(encrypted) != '[object ArrayBuffer]') {
      throw new Error('encrypted must is ArrayBuffer');
    }

    let key = await crypto.subtle.importKey('raw', new TextEncoder().encode(keyRaw), 'AES-GCM', true, ['encrypt', 'decrypt']);
    let decrypted = '';
    try {
      decrypted = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, encrypted);
    } catch (error) {
      throw new Error(error);
      return '';
    }
    let decoded = new TextDecoder().decode(decrypted);
    return decoded;
  }
};

 

3. 示例

(async () => {
  let iv = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
  let keyRaw = 'IHieUI!%9awQqszp';

  // 1.encrypt test
  let ciphertext = await aesGcmHelper.encrypt('hello, Tom', keyRaw, iv);
  // - hex
  let ciphertextOfHex = aesGcmHelper.buf2Hex(ciphertext);
  console.log(ciphertextOfHex); // => b85320c50c256bc27dc1113a92a83614d60161f6af4d1ce237b0
  // - base64
  let ciphertextOfBase64 = aesGcmHelper.buf2Base64(ciphertext);
  console.log(ciphertextOfBase64); // => uFMgxQwla8J9wRE6kqg2FNYBYfavTRziN7A=

  // 2.decrypt test
  // - hex
  let decodedData = await aesGcmHelper.decrypt(aesGcmHelper.hex2Buf(ciphertextOfHex), keyRaw, iv);
  console.log(decodedData); // => hello, Tom
  // - base64
  decodedData = await aesGcmHelper.decrypt(aesGcmHelper.base64ToBuf(ciphertextOfBase64), keyRaw, iv);
  console.log(decodedData); // => hello, Tom
})();

  

 

posted on 2023-06-21 16:57  FangMu  阅读(908)  评论(1编辑  收藏  举报