前端加解密之 Crypto

Web Crypto API

之前写过 NodeJS 加解密之 crypto 模块,前端一般很少用这个功能,就我见过的业务来讲基本上没人用,了解了下前端浏览器支持的 Crypto 和 Node.js 模块支持的 Crypto API 还不太一样。

前端的 Crypto API 挂在浏览器的当前上下文,直接通过 crypto 就能访问。

包含一个属性两个方法:

graph LR;
A[Crypto]-->|方法|B[RandomSource];
A[Crypto]-->|属性|C[subtle];
B-->D[Crypto.getRandomValues];
B-->E[randomUUID];

randomUUID 生成 v4 的 UUID 是很有用的 API,关于加解密的全部都在 subtle 属性上,使用 subtle 一定要注意下面提示的两点。

此功能:

  1. 仅在安全上下文(HTTPS)中可用
  2. 在某些或所有支持的浏览器中可用。

加密方式

前端浏览器的 Crypto 技术提供了以下常用加密方式:

1. 对称加密和解密

对称加密是一种加密方式,使用同一个密钥进行加密和解密。Web Crypto API 提供了多种对称加密算法,如 AES、DES 和 3DES 等,可以通过这些算法来加密和解密数据。

实现过程中用到的 Web API 参考:

所有 API 中最重要的是 SubtleCrypto.generateKey() API,SubtleCrypto.generateKey() 是 Web Cryptography API 中的一个方法,用于生成密钥。

该方法有三个参数:

下面演示对称加密和解密的过程:

async function encryptSymmetricKey(key, iv, data) {
  const ciphertext = await window.crypto.subtle.encrypt(
    {
      name: "AES-GCM",
      iv,
    },
    key,
    data
  );
  return ciphertext;
}

async function decryptSymmetricKey(key, iv, ciphertext) {
  const plaintext = await window.crypto.subtle.decrypt(
    {
      name: "AES-GCM",
      iv,
    },
    key,
    ciphertext
  );
  const decoder = new TextDecoder();
  return decoder.decode(plaintext);
}

async function symmetricEncryptionDecryption() {
  const text = "Shavahn";

  // 生成一个随机的对称加密密钥
  const key = await window.crypto.subtle.generateKey(
    {
      name: "AES-GCM",
      length: 256,
    },
    true,
    ["encrypt", "decrypt"]
  );

  // 将文本编码为 ArrayBuffer
  const encoder = new TextEncoder();
  const data = encoder.encode(text);

  // 加密文本
  const iv = window.crypto.getRandomValues(new Uint8Array(12));
  const ciphertext = await encryptSymmetricKey(key, iv, data);

  // 将加密后的文本解密
  const decryptedText = await decryptSymmetricKey(key, iv, ciphertext);

  console.log("原始文本:", text);
  console.log("加密后的文本:", new Uint8Array(ciphertext));
  console.log("解密后的文本:", decryptedText);
}

symmetricEncryptionDecryption();

以下是代码的解析:

  1. async function encryptSymmetricKey(key, iv, data):这是一个异步函数,用于加密传入的数据。它使用传入的对称密钥和初始化向量 iv,以及 AES-GCM 加密算法对数据进行加密,并返回加密后的结果 ciphertext。
  2. async function decryptSymmetricKey(key, iv, ciphertext):这是一个异步函数,用于解密传入的密文。它使用传入的对称密钥和初始化向量 iv,以及 AES-GCM 加密算法对密文进行解密,并将解密后的结果以字符串形式返回。
  3. async function symmetricEncryptionDecryption():这是一个异步函数,它执行了整个加密和解密的过程。它首先生成一个随机的 AES-GCM 对称密钥,并将要加密的文本编码为 ArrayBuffer。然后,它生成一个随机的 12 字节初始化向量 iv,并使用 encryptSymmetricKey 函数对文本进行加密。最后,它使用 decryptSymmetricKey 函数对密文进行解密,并输出原始文本、加密后的文本和解密后的文本。
  4. window.crypto.subtle:这是 Web Crypto API 提供的加密和解密函数的命名空间。它提供了一组对称和非对称加密算法,包括 AES-GCM、RSA-OAEP 等。
  5. TextEncoderTextDecoder:这两个对象用于将文本字符串编码为 ArrayBuffer 和将 ArrayBuffer 解码为文本字符串。在这个例子中,我们使用它们将文本字符串编码为 ArrayBuffer,以便进行加密,并将解密后的 ArrayBuffer 解码为文本字符串。

2. 非对称加密和解密

非对称加密是一种加密方式,使用一对公钥和私钥进行加密和解密。Web Crypto API 提供了多种非对称加密算法,如 RSA 和 ECDSA 等,可以通过这些算法来加密和解密数据。

async function encryptAsymmetricKey(publicKey, data) {
  const encodedData = new TextEncoder().encode(data);
  const encryptedData = await window.crypto.subtle.encrypt(
    {
      name: "RSA-OAEP",
    },
    publicKey,
    encodedData
  );
  return encryptedData;
}

async function decryptAsymmetricKey(privateKey, encryptedData) {
  const decryptedData = await window.crypto.subtle.decrypt(
    {
      name: "RSA-OAEP",
    },
    privateKey,
    encryptedData
  );
  return new TextDecoder().decode(decryptedData);
}

async function asymmetricEncryptionDecryption() {
  const text = "Shavahn";

  // 生成一对非对称加密密钥
  const keyPair = await window.crypto.subtle.generateKey(
    {
      name: "RSA-OAEP",
      modulusLength: 2048,
      publicExponent: new Uint8Array([1, 0, 1]),
      hash: { name: "SHA-256" },
    },
    true,
    ["encrypt", "decrypt"]
  );

  // 加密文本
  const encryptedData = await encryptAsymmetricKey(keyPair.publicKey, text);

  // 将加密后的文本解密
  const decryptedText = await decryptAsymmetricKey(
    keyPair.privateKey,
    encryptedData
  );

  console.log("原始文本:", text);
  console.log("加密后的文本:", new Uint8Array(encryptedData));
  console.log("解密后的文本:", decryptedText);
}

asymmetricEncryptionDecryption();

3. 数字签名和验证

数字签名是一种用于验证数据完整性和身份认证的技术,Web Crypto API 提供了多种数字签名算法,如 RSA 和 ECDSA 等,可以通过这些算法来对数据进行签名和验证。

async function signMessage(privateKey, message) {
  const encoder = new TextEncoder();
  const data = encoder.encode(message);

  const signature = await window.crypto.subtle.sign(
    {
      name: "ECDSA",
      hash: { name: "SHA-256" },
    },
    privateKey,
    data
  );
  return signature;
}

async function verifySignature(publicKey, message, signature) {
  const encoder = new TextEncoder();
  const data = encoder.encode(message);

  const isVerified = await window.crypto.subtle.verify(
    {
      name: "ECDSA",
      hash: { name: "SHA-256" },
    },
    publicKey,
    signature,
    data
  );
  return isVerified;
}

async function signatureAndVerification() {
  const message = "hello world";

  // 生成椭圆曲线密钥对
  const keyPair = await window.crypto.subtle.generateKey(
    {
      name: "ECDSA",
      namedCurve: "P-256",
    },
    true,
    ["sign", "verify"]
  );

  // 对消息进行数字签名
  const signature = await signMessage(keyPair.privateKey, message);

  // 验证数字签名
  const isVerified = await verifySignature(keyPair.publicKey, message, signature);

  console.log("原始消息:", message);
  console.log("数字签名:", new Uint8Array(signature));
  console.log("验证结果:", isVerified);
}

signatureAndVerification();

如何把密钥发给后端

前端通过 window.crypto.subtle.generateKey 生成的公钥可以通过以下步骤发送给后端:

  1. 从生成的密钥对中获取公钥,使用 exportKey 方法将其导出成 ArrayBuffer 格式。
const keyPair = await window.crypto.subtle.generateKey(
  { name: "RSA-OAEP", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256" },
  true,
  ["encrypt", "decrypt"]
);

const publicKey = await window.crypto.subtle.exportKey(
  "spki",
  keyPair.publicKey
);
  1. 将导出的 ArrayBuffer 转换为 Base64 编码的字符串格式,以便可以通过 HTTP 请求发送给后端。
const publicKeyBase64 = btoa(String.fromCharCode.apply(null, new Uint8Array(publicKey)));
  1. 将 Base64 编码的字符串作为请求参数发送到后端,后端可以将其解码并使用相应的加密算法进行加密操作。
// 发送公钥给后端
fetch('/api/sendPublicKey', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ publicKey: publicKeyBase64 }),
})

上面就是简单的一种演示,只要用 exportKey 拿到 Key,你想怎么发送就怎么发送。

摘要算法

async function generateHMAC(key, data) {
  const encoder = new TextEncoder();
  const encodedKey = encoder.encode(key);
  const encodedData = encoder.encode(data);

  const cryptoKey = await window.crypto.subtle.importKey(
    "raw",
    encodedKey,
    {
      name: "HMAC",
      hash: "SHA-256"
    },
    false,
    ["sign"]
  );

  const signature = await window.crypto.subtle.sign(
    {
      name: "HMAC",
      hash: "SHA-256"
    },
    cryptoKey,
    encodedData
  );

  return signature;
}

async function verifyHMAC(key, data, signature) {
  const encoder = new TextEncoder();
  const encodedKey = encoder.encode(key);
  const encodedData = encoder.encode(data);

  const cryptoKey = await window.crypto.subtle.importKey(
    "raw",
    encodedKey,
    {
      name: "HMAC",
      hash: "SHA-256"
    },
    false,
    ["verify"]
  );

  const result = await window.crypto.subtle.verify(
    {
      name: "HMAC",
      hash: "SHA-256"
    },
    cryptoKey,
    signature,
    encodedData
  );

  return result;
}

async function hmacExample() {
  const key = "my secret key";
  const data = "Shavahn";
  
  const signature = await generateHMAC(key, data);
  console.log("HMAC 签名:", new Uint8Array(signature));

  const isValid = await verifyHMAC(key, data, signature);
  console.log("HMAC 签名是否有效:", isValid);
}

hmacExample();

主要用途

前端浏览器的 Crypto 技术可以用于以下主要用途:

1. 安全的数据传输

通过使用前端浏览器的 Crypto 技术,开发者可以对数据进行加密和解密,从而确保数据在传输过程中的安全性。例如,在使用 HTTPS 协议时,可以使用浏览器内置的加密算法对传输的数据进行加密,以保证数据的机密性和完整性。

2. 数字签名验证

通过使用前端浏览器的 Crypto 技术,可以对数据进行数字签名和验证,从而确保数据的完整性和真实性。例如,在电子商务中,可以使用数字签名技术对交易数据进行签名和验证,以保证交易数据的真实性和完整性。

3. 认证和授权

通过使用前端浏览器的 Crypto 技术,可以对用户进行认证和授权。例如,在使用 OAuth2 认证时,可以使用前端浏览器的 Crypto 技术生成安全的随机数,以保证授权码的安全性。

总结

前端浏览器的 Crypto 技术是实现安全的前端应用程序的关键技术之一。Web Crypto API 提供了多种密码学算法和函数,使得前端开发人员可以在浏览器中实现安全的数据传输、数字签名和验证等功能,同时确保数据的安全性和完整性。使用前端浏览器的 Crypto 技术可以使开发人员更轻松地实现密码学相关的功能,同时提高应用程序的安全性和保护用户数据的隐私。因此,开发人员应该了解和熟悉前端浏览器的 Crypto 技术,并在必要时使用它来保证应用程序的安全性。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容

  • [TOC] 前端加解密 参考文档:SubtleCrypto:https://developer.mozilla.o...
    johnzhu12阅读 574评论 0 0
  • H5视频播放器内核的开发对于前端工程师来说算是一个比较少涉足的领域,恰好工作中有所研究,分享出来给感兴趣的同学。简...
    陈金Marlon阅读 19,456评论 7 18
  • 前言开门见山,这篇文章,适合 「 初级前端 」 ,如果你还在校招的话,或者还在求职的话,可以看看本文,找一找灵感,...
    WEB前端含光阅读 1,337评论 1 2
  • 理解 HTTP 协议对构建网络应用是一个非常基础的要求,比如爬虫类程序,必须深入理解 Request 和 Reso...
    马六甲的笔记阅读 451评论 0 0
  • HTTP——超级文本传输协议,用于传输超媒体文档(如Html)的应用层协议,典型的应用场景是浏览器网页、API接口...
    jxiang112阅读 330评论 0 0