js 加密和解密

js加密和解密

window.crypto.subtle(也称为 crypto.subtle)提供了加密和解密字符串的能力,但请注意,它是一个低级别的 API,需要你自己处理密钥管理、编码转换等工作。下面是一个简单的例子,展示如何使用 AES-GCM 模式来加密和解密字符串。

这里实现这样的效果

function encrypt(key='string', data='string');

function decrypt(key='string', data='string');

为了实现这样的 encrypt​ 和 decrypt​ 函数,这涉及到几个步骤:生成密钥、将字符串转换为 ArrayBuffer、加密或解密数据、并将结果转换回字符串。

以下是完整的实现:

生成密钥

由于我们希望 encrypt​ 和 decrypt​ 函数能够接受字符串形式的密钥,我们需要首先将字符串形式的密钥转换为合适的密钥对象。这里我们假设密钥字符串是经过某种方式生成的,并且已经被正确地转换为密钥对象。

async function generateKeyFromPassword(password) {
  // 密钥生成算法,这里使用 PBKDF2 从密码派生密钥
  const salt = window.crypto.getRandomValues(new Uint8Array(16));
  const keyMaterial = await window.crypto.subtle.importKey(
    "raw",
    salt,
    { name: "PBKDF2", hash: { name: "SHA-256" }, salt, iterations: 100000 },
    false,
    ["deriveBits"]
  );
  const key = await window.crypto.subtle.deriveBits(
    {
      name: "PBKDF2",
      salt,
      iterations: 100000,
      hash: { name: "SHA-256" }
    },
    keyMaterial,
    256, // 密钥长度
    true
  );
  return await window.crypto.subtle.importKey(
    "raw",
    new Uint8Array(key),
    { name: "AES-GCM" },
    true,
    ["encrypt", "decrypt"]
  );
}

加密字符串

async function encrypt(key, data) {
  const encoder = new TextEncoder(); // 用于编码字符串到 ArrayBuffer
  const encodedData = encoder.encode(data);

  const iv = window.crypto.getRandomValues(new Uint8Array(12)); // 生成初始向量
  const encryptedData = await window.crypto.subtle.encrypt(
    { name: "AES-GCM", iv }, // 配置选项
    key,
    encodedData
  );

  // 将密文和 IV 结合在一起
  const combinedData = new Uint8Array(iv.length + encryptedData.byteLength);
  combinedData.set(iv);
  combinedData.set(new Uint8Array(encryptedData), iv.length);

  return btoa(String.fromCharCode(...combinedData)); // 将 ArrayBuffer 转换为 Base64 编码的字符串
}

解密字符串

async function decrypt(key, data) {
  const decodedData = atob(data); // 将 Base64 编码的字符串转换回 ArrayBuffer
  const combinedData = new Uint8Array(decodedData.length);
  for (let i = 0; i < decodedData.length; i++) {
    combinedData[i] = decodedData.charCodeAt(i);
  }

  const iv = combinedData.slice(0, 12);
  const encryptedData = combinedData.slice(12);

  const decryptedData = await window.crypto.subtle.decrypt(
    { name: "AES-GCM", iv }, // 配置选项
    key,
    encryptedData
  );

  const decoder = new TextDecoder(); // 用于解码 ArrayBuffer 到字符串
  return decoder.decode(decryptedData);
}

使用这些函数

(async () => {
  try {
    const password = "my-secret-password"; // 示例密码
    const key = await generateKeyFromPassword(password);
    const originalText = "Hello, world!";

    const encryptedText = await encrypt(key, originalText);
    console.log("Encrypted:", encryptedText);

    const decryptedText = await decrypt(key, encryptedText);
    console.log("Decrypted:", decryptedText);
  } catch (error) {
    console.error("Error:", error);
  }
})();

注意事项

  • 这个示例使用了 PBKDF2​ 从密码派生密钥,并使用 AES-GCM​ 模式进行加密。这是一个较为安全的做法,但请注意,密码派生过程可能会比较耗时。
  • 为了方便起见,这里的 encrypt​ 和 decrypt​ 函数直接使用 Base64 编码来表示加密后的数据。这种方法适合在网络上传输,但请注意 Base64 编码会增加数据的大小。
  • 在实际应用中,密钥管理是非常重要的,应确保密钥的安全性,并考虑使用更复杂的方法来保护密码,例如使用更安全的密码派生函数(如 argon2​)。
image.png

留下你的脚步
推荐阅读