0

web端也支持Crypto加密对象了

 1 year ago
source link: https://www.haorooms.com/post/web_crypto
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

之前文章有写关于加密的javascript用户密码加密,js密码加密,是很久之前写的了,有点过时。其实web支持前端加密了,喜大普奔,可以不用引入js进行前端加密。

Crypto对象

我们看下Crypto对象的兼容性

enter image description here

crypto.subtle对象

crypto 对象还支持一个名为 subtle 的属性,目前唯一的属性,返回的属性值是一个对象,称为 SubtleCrypto 对象,可以用来生成各种签名和密钥,所有方法均返回 Promise,包括:

SubtleCrypto.encrypt()
SubtleCrypto.decrypt()
SubtleCrypto.sign()
SubtleCrypto.verify()
SubtleCrypto.digest()
SubtleCrypto.generateKey()
SubtleCrypto.deriveKey()
SubtleCrypto.deriveBits()
SubtleCrypto.importKey()
SubtleCrypto.exportKey()
SubtleCrypto.wrapKey()
SubtleCrypto.unwrapKey()

SubtleCrypto.encrypt()

var result = crypto.encrypt(algo, key, cleartext);

algo 是一个使用加密函数的对象或者 DOMString,后者是 {"name": algo} 的缩写。支持的值是:

{"name": "AES-CBC", iv} iv 是具有16个随机字节的 ArrayBuffer 或 ArrayBufferView (这些应该由 RandomSource.getRandomValues() 生成)。

{"name": "AES-CTR", counter, length}

{"name": "AES-GCM", iv, additionalData, tagLength} (additionalData 和 tagLength 是可选的)

{"name": "RSA-OAEP", label} (label 是可选的)

key 是一个包含签名密钥的 CryptoKey。

cleartext 是一个包含需要加密的明文 ArrayBuffer 或者 ArrayBufferView 对象。

(() => {

  /*
  Store the calculated ciphertext and counter here, so we can decrypt the message later.
  */
  let ciphertext;
  let counter;

  /*
  Fetch the contents of the "message" textbox, and encode it
  in a form we can use for the encrypt operation.
  */
  function getMessageEncoding() {
    const messageBox = document.querySelector("#aes-ctr-message");
    let message = messageBox.value;
    let enc = new TextEncoder();
    return enc.encode(message);
  }

  /*
  Get the encoded message, encrypt it and display a representation
  of the ciphertext in the "Ciphertext" element.
  */
  async function encryptMessage(key) {
    let encoded = getMessageEncoding();
    // The counter block value must never be reused with a given key.
    counter = window.crypto.getRandomValues(new Uint8Array(16)),
    ciphertext = await window.crypto.subtle.encrypt(
      {
        name: "AES-CTR",
        counter,
        length: 64
      },
      key,
      encoded
    );

    let buffer = new Uint8Array(ciphertext, 0, 5);
    const ciphertextValue = document.querySelector(".aes-ctr .ciphertext-value");
    ciphertextValue.classList.add('fade-in');
    ciphertextValue.addEventListener('animationend', () => {
      ciphertextValue.classList.remove('fade-in');
    });
    ciphertextValue.textContent = `${buffer}...[${ciphertext.byteLength} bytes total]`;
  }

  /*
  Fetch the ciphertext and decrypt it.
  Write the decrypted message into the "Decrypted" box.
  */
  async function decryptMessage(key) {
    let decrypted = await window.crypto.subtle.decrypt(
      {
        name: "AES-CTR",
        counter,
        length: 64
      },
      key,
      ciphertext
    );

    let dec = new TextDecoder();
    const decryptedValue = document.querySelector(".aes-ctr .decrypted-value");
    decryptedValue.classList.add('fade-in');
    decryptedValue.addEventListener('animationend', () => {
      decryptedValue.classList.remove('fade-in');
    });
    decryptedValue.textContent = dec.decode(decrypted);
  }

  /*
  Generate an encryption key, then set up event listeners
  on the "Encrypt" and "Decrypt" buttons.
  */
  window.crypto.subtle.generateKey(
    {
        name: "AES-CTR",
        length: 256
    },
    true,
    ["encrypt", "decrypt"]
  ).then((key) => {
    const encryptButton = document.querySelector(".aes-ctr .encrypt-button");
    encryptButton.addEventListener("click", () => {
      encryptMessage(key);
    });

    const decryptButton = document.querySelector(".aes-ctr .decrypt-button");
    decryptButton.addEventListener("click", () => {
      decryptMessage(key);
    });
  });
})();

SubtleCrypto.sign()

//sign

signature = await window.crypto.subtle.sign(
  {
    name: "RSA-PSS",
    saltLength: 32,
  },
  privateKey,
  '加密数据data'
);

详细请看:https://developer.mozilla.org/zh-CN/docs/Web/API/Crypto

getRandomValues

上文有提到getRandomValues和 Math.random() 方法的区别在于,getRandomValues() 方法的随机种子生成器更加的无序,例如系统层面的无序源(有些硬件自带随机种子)。

然后不同浏览器下 getRandomValues() 方法生成的随机数可能是有区别的。

以及 getRandomValues() 方法的底层实现是没有缓存的,随机数都是实时生成的,因此,性能上是要比 Math.random() 差的,因此,如果是高并发的场景,同时随机数仅仅是用做随机,与安全和金钱不相关,请使用 Math.random() 而不是 getRandomValues()。

let randNumber = self.crypto.getRandomValues(new Uint32Array(1))[0];
// 一串随机整数,通常10位
console.log(randNumber);

UUID生成

除了生成随机数,Crypto对象还可以用来生成字符长度为36的 UUID (Universally Unique Identifier的缩写,表示唯一通用标识符)

let uuid = self.crypto.randomUUID();
console.log(uuid);
// 示意输出:2433df46-d77f-4eb9-bbdd-4cd99361fe08

不过这个 API 只有Chrome 92+,才支持。

关于uuid的生产,我前面有文章介绍 https://www.haorooms.com/post/nanoid_uuid

感兴趣的可以对比看一看。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK