在过去很长一段时间里,只要一提到在前端进行加密、解密、生成哈希(MD5/SHA)或生成随机数,绝大多数开发者的第一反应就是 npm install crypto-js。作为曾经的功勋库,crypto-js 陪伴我们度过了无数个项目。
然而,随着 Web 标准的演进,现代浏览器早已内置了一个更强大、更安全、性能也更恐怖的官方武器——Web Crypto API。时至今日,如果你还在新项目里打包第三方的加密库,是时候了解一下为什么要换成原生 API 了。
Web Crypto API 是浏览器原生提供的一套加密接口,允许 JavaScript 脚本使用密码学原语来构建基于加密的系统。它自 2015 年 7 月起已得到各大浏览器的广泛支持,是一个相当成熟且稳定的 Web 标准。
在 Web Crypto 规范诞生前,部分浏览器为了满足加密需求,在全局实现了一个名为 Crypto 的接口(即 window.crypto)。但正由于这样,当时各大浏览器的实现缺乏统一的 W3C 标准,定义模糊,且在密码学意义上是不够严谨和安全的(not cryptographically sound)。
当正式的 Web Crypto API 标准(支持 encrypt、decrypt、sign、verify 等现代异步加密操作)推出时,为了彻底避免和老旧、不安全的旧接口方法发生命名混淆与冲突,W3C 规范决定将这些低底层的密码学核心方法群全部打包进一个全新的独立接口中,这就是 SubtleCrypto。你可以通过全局对象的 crypto.subtle 属性来访问它。
由于旧的全局 crypto 对象并没有被废除(它依然保留了诸如密码学安全的伪随机数生成器 crypto.getRandomValues() 和 crypto.randomUUID() 等简单的同步方法),因此所有新版的 SubtleCrypto 复杂加密算法全部被挂载到了它的下级属性中。你必须通过 crypto.subtle 属性才能访问到这一整套现代加解密方法。
Node.js 最早在 v15.0.0 引入了 Web Crypto API 接口。直到 Node.js 19.0.0,官方正式去掉了实验性警告,将其标记为 稳定版本(Stable),并且默认在全局作用域挂载了 globalThis.crypto。
在决定是否在 Web 项目中使用 Web Crypto API 时,通常是因为它相比传统的第三方加密库(如 CryptoJS, jsencrypt, forge 等)在安全性、性能和原生支持度上带来了降维打击的优势。
使用 Web Crypto API 有唯一的限制条件:
Web Crypto API(通常称为 crypto.subtle)的核心接口主要由以下 5 个标准接口(Interfaces) 和 1 个全局入口 组成。它们构成了浏览器原生密码学生态的基石。
虽然这不是单独的加解密接口,但它是所有 Web 加密功能的总入口,挂载在全局 window.crypto(或 globalThis.crypto)上。Crypto提供基础的加密功能,主要包括:
这是整个 Web Crypto API 中最重要、最核心的接口。所有的实际异步加解密、签名、密钥派生等操作都在这里执行。SubtleCrypto 提供的主要方法包括:
该接口代表一个在浏览器中生成的、受保护的密钥对象。为了安全,浏览器的 SubtleCrypto 方法不直接操作纯文本的密钥字符串(如 "my-secret-key"),而是操作 CryptoKey 实例。
当使用 RSA、ECDSA 等非对称加密算法时,crypto.subtle.generateKey() 返回的不是单个密钥,而是一个包含公钥和私钥的 CryptoKeyPair 对象。
虽然它在技术上是一个规范定义的字典对象(Dictionary),而不是传统的类或接口,但它是每个 API 都必传的灵魂参数。
| 接口名称 | 角色定位 | 非安全上下文(HTTP)使用规则 | 典型返回值 / 用途 |
|---|---|---|---|
| Crypto | 全局大管家 | 是(部分方法受限) | 提供crypto.subtle入口、用于生成UUID |
| SubtleCrypto | 核心工具库 | 否(必须在HTTPS/localhost环境下使用) | 返回Promise<ArrayBuffer>,用于生成密文、哈希等加密核心操作 |
| CryptoKey | 密钥黑盒 | 否 | 作为安全载体,直接传递给加解密函数使用 |
| CryptoKeyPair | 密钥对容器 | 否 | 封装返回包含publicKey和privateKey的密钥对对象 |
| 算法标识符 | 支持的操作 | 推荐搭配与技术参数 | 典型应用场景 |
|---|---|---|---|
| AES-GCM(强烈推荐) | encrypt、decrypt、generateKey、importKey、exportKey、wrapKey、unwrapKey | 密钥支持128/192/256位,IV推荐96位(12字节) | 传输数据加解密、本地缓存加密,自带防篡改认证标签 |
| AES-CBC | encrypt、decrypt、generateKey、importKey、exportKey、wrapKey、unwrapKey | 密钥支持128/192/256位,IV固定128位(16字节) | 兼容老旧遗留系统的对称加解密业务 |
| AES-CTR | encrypt、decrypt、generateKey、importKey、exportKey、wrapKey、unwrapKey | 计数器模式,支持流式处理 | 高频非对齐的流式数据区块加解密 |
| 算法标识符 | 支持的操作 | 推荐搭配与技术参数 | 典型应用场景 |
|---|---|---|---|
| RSA-OAEP(强烈推荐) | encrypt, decrypt, generateKey, importKey, exportKey, wrapKey, unwrapKey | 模数:2048, 4096 位哈希:推荐 SHA-256 | 混合加密体系中用于加密“对称密钥”,或加密超短敏感字段 |
RSA-OAEP是Web场景下非对称加密的首选方案,它通过最优非对称加密填充机制,解决了传统“教科书RSA”的密文可延展性安全缺陷,在随机预言模型下可以抵御适应性选择密文攻击。由于非对称加密的特性,它不适合直接加密大体积数据,行业通用的最佳实践就是用它加密AES等对称密钥,再用对称算法加密实际业务数据,兼顾安全性和性能效率。
| 算法名称 | 支持的操作 | 推荐搭配与技术参数 | 典型应用场景 |
|---|---|---|---|
| ECDSA(强烈推荐) | sign、verify、generateKey、importKey、exportKey | 椭圆曲线:P-256、P-384、P-521 | 现代高并发身份验签、JWT签名、区块链节点验签 |
| RSASSA-PKCS1-v1_5 | sign、verify、generateKey、importKey、exportKey | 模数≥2048位,哈希推荐SHA-256 | 传统企业级API签名、老旧版本JWT签名验签 |
| RSA-PSS | sign、verify、generateKey、importKey、exportKey | RSA概率签名方案,密码学严谨度极高 | 金融级或司法级等高安全要求的数字签名场景 |
| HMAC | sign、verify、generateKey、importKey、exportKey | 基于哈希的密钥消息认证码 | 前后端API请求防篡改哈希签名,如AWS签名机制 |
| 算法名称 | 支持的操作 | 推荐搭配与技术参数 | 典型应用场景 |
|---|---|---|---|
| SHA-256 | digest | 无需密钥,单向不可逆 | 文件完整性校验(秒传场景)、数据防篡改指纹生成 |
| SHA-384 | digest | 无需密钥,单向不可逆,安全强度高于SHA-256 | 高敏感数据校验、金融级业务的完整性验证 |
| SHA-512 | digest | 无需密钥,单向不可逆,安全强度最高 | 司法级、军工级等高安全要求场景的哈希校验 |
这三款同属SHA-2家族哈希算法,都具备单向不可逆的核心特性,无法从最终哈希值反推原始输入内容,仅用于生成数据唯一指纹,不能直接用于加解密还原原始数据。安全强度随输出位数提升而升高,同时计算性能也会小幅下降,普通业务场景优先选择SHA-256即可满足需求。
| 算法名称 | 支持的操作 | 推荐搭配与技术参数 | 典型应用场景 |
|---|---|---|---|
| ECDH | deriveKey、deriveBits、generateKey、importKey、exportKey | 椭圆曲线:Diffie-Hellman | 两个终端在不安全信道中,安全地协商出一致的对称密钥 |
| PBKDF2 | deriveKey、deriveBits、importKey | 基于密码的密钥派生,推荐迭代≥100000次 | 将用户输入的简短文明密码,转换为符合AES标准的高强度密钥 |
| HKDF | deriveKey、deriveBits、importKey | 基于HMAC的密钥扩展算法 | 从一个基础主密钥,安全地扩展派生出多个子密钥 |
| 算法名称 | 不支持原因 | 替代实现方案 |
|---|---|---|
| MD5 | 已被密码学界证实存在碰撞漏洞,安全性完全失效 | 引入crypto-js等第三方库实现或使用 SHA-256 |
| SM2/SM3/SM4国密系列 | 浏览器国际标准未原生内置国密算法 | 引入sm-crypto等国密第三方库实现 |
| ChaCha20-Poly1305/Ed25519 | 目前尚未完全普及到所有主流浏览器的 crypto.subtle 中 | 引入第三方加密库补充实现 |
以下是整理的 Web Crypto API (crypto.subtle) 最核心、最常用的四大标准代码示例(哈希生成、密钥生成、加密、解密)。需要特别提醒的是:所有 crypto.subtle 的操作都是异步的,都会返回一个 Promise,因此必须配合 async/await 使用。
AES-GCM (Galois/Counter Mode) 是一种非常流行且强大的认证加密(AEAD, Authenticated Encryption with Associated Data) 模式。你可以把它理解为一种“二合一”的加密模式:它在高效加密数据的同时,还能确保数据没有被篡改。
AES-GCM 是兼具高性能和高安全性的认证加密模式,特别适合在 Web Crypto API 这类原生环境中使用。它的安全性高度依赖于每次加密都使用唯一且不可预测的 IV。遵循这一核心原则,并妥善管理密钥、认真验证认证标签,就能为你的 Web 应用构建起一道坚实的数据安全防线。
/**
* 计算字符串的 SHA-256 哈希值
* @param {string} text - 输入的文本
* @returns {Promise<string>} Hex 格式的哈希字符串
*/
async function generateHash(text) {
// 1. 将文本编码为 Uint8Array 字节流
const encoder = new TextEncoder();
const data = encoder.encode(text);
// 2. 使用 crypto.subtle.digest 生成哈希(返回 ArrayBuffer)
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
// 3. 将 ArrayBuffer 转换为常见的十六进制 (Hex) 字符串
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
return hashHex;
}
// 使用示例:
const sha256Result = await generateHash("Hello World");
/**
* 生成一个用于 AES-GCM 加密的 256 位随机密钥
* @returns {Promise<CryptoKey>} 返回 CryptoKey 对象
*/
async function generateAESKey() {
return await crypto.subtle.generateKey(
{
name: 'AES-GCM',
length: 256 // 密钥长度支持 128, 192, 或 256 位
},
false, // 是否允许该密钥被导出(如果设为 false,密钥无法脱离当前浏览器,安全性极高)
['encrypt', 'decrypt'] // 该密钥被允许用来做什么操作
);
}
/**
* 对文本进行 AES-GCM 加密
* @param {string} text - 待加密的明文
* @param {CryptoKey} key - 上一步生成的 CryptoKey 对象
* @returns {Promise<{cipherText: ArrayBuffer, iv: Uint8Array}>} 返回密文和IV
*/
async function encryptText(text, key) {
const encoder = new TextEncoder();
const encodedText = encoder.encode(text);
// AES-GCM 推荐使用 12位(96位)的高安全伪随机数作为 IV
// 注意:这里使用的是外部同步方法 getRandomValues 生成随机数
const iv = crypto.getRandomValues(new Uint8Array(12));
// 执行加密
const cipherText = await crypto.subtle.encrypt(
{
name: 'AES-GCM',
iv: iv // 加密参数必须包含 IV
},
key, // 密钥
encodedText // 明文数据
);
// 需要同时把密文和 IV 返回,因为解密时必须提供一模一样的 IV
return { cipherText, iv };
}
/**
* 对 AES-GCM 密文进行解密
* @param {ArrayBuffer} cipherText - 密文数据
* @param {CryptoKey} key - 加密时使用的同一个密钥
* @param {Uint8Array} iv - 加密时对应的随机 IV
* @returns {Promise<string>} 解密后的明文字符串
*/
async function decryptText(cipherText, key, iv) {
// 执行解密
const decryptedBuffer = await crypto.subtle.decrypt(
{
name: 'AES-GCM',
iv: iv // 必须传入与加密时完全相同的 IV
},
key,
cipherText
);
// 将 ArrayBuffer 字节解码还原为字符串
const decoder = new TextDecoder();
return decoder.decode(decryptedBuffer);
}
非对称加密使用公钥加密、私钥解密,常用于非对称场景或加密对称密钥(如密钥交换)。这里使用最安全的 RSA-OAEP 算法。
/**
* 生成用于 RSA-OAEP 加解密的 2048 位密钥对
* @returns {Promise<CryptoKeyPair>} 返回包含公钥和私钥的对象
*/
async function generateRSAKeyPair() {
return await crypto.subtle.generateKey(
{
name: "RSA-OAEP",
modulusLength: 2048, // 密钥长度,2048 位是目前的标准安全线
publicExponent: new Uint8Array([1, 0, 1]), // 等于 65537,固定的密码学标准常数
hash: "SHA-256", // 内部使用的哈希算法
},
true, // 是否允许导出
["encrypt", "decrypt"] // 用途分配
);
}
/**
* 使用 RSA 公钥加密文本
*/
async function rsaEncrypt(text, publicKey) {
const encoder = new TextEncoder();
const data = encoder.encode(text);
// 返回 ArrayBuffer 格式的密文
return await crypto.subtle.encrypt(
{ name: "RSA-OAEP" },
publicKey,
data
);
}
/**
* 使用 RSA 私钥解密文本
*/
async function rsaDecrypt(cipherText, privateKey) {
const decryptedBuffer = await crypto.subtle.decrypt(
{ name: "RSA-OAEP" },
privateKey,
cipherText
);
const decoder = new TextDecoder();
return decoder.decode(decryptedBuffer);
}
数字签名常用于防止数据被篡改并确认发送者身份。其逻辑是:私钥签名、公钥验证。这里使用最通用的 RSASSA-PKCS1-v1_5 算法。
/**
* 生成用于数字签名的 RSA 密钥对
*/
async function generateSignKeyPair() {
return await crypto.subtle.generateKey(
{
name: "RSASSA-PKCS1-v1_5",
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256",
},
true,
["sign", "verify"] // 注意:这里的用途是签名和验证
);
}
/**
* 使用私钥对数据生成签名
* @returns {Promise<ArrayBuffer>} 返回签名摘要
*/
async function signData(text, privateKey) {
const encoder = new TextEncoder();
const data = encoder.encode(text);
return await crypto.subtle.sign(
{ name: "RSASSA-PKCS1-v1_5" },
privateKey,
data
);
}
/**
* 使用公钥验证签名是否有效
* @returns {Promise<boolean>} 验证成功返回 true,被篡改则返回 false
*/
async function verifyData(text, signature, publicKey) {
const encoder = new TextEncoder();
const data = encoder.encode(text);
return await crypto.subtle.verify(
{ name: "RSASSA-PKCS1-v1_5" },
publicKey,
signature, // 之前生成的签名 ArrayBuffer
data // 原始数据
);
}
在实际业务中,密钥不能只存在于浏览器内存中,我们需要将其导出为字符串传给后端,或者将后端的字符串密钥导入为浏览器可识别的 CryptoKey 对象。
Web Crypto 支持多种格式,最常用的是 JWK (JSON Web Key) 格式(易读、跨平台)和 raw/spki/pkcs8(二进制字节流)。
/**
* 将 CryptoKey 密钥对象导出为标准的 JSON 对象 (JWK)
* @param {CryptoKey} key
* @returns {Promise<Object>} 可直接用于传输或 JSON.stringify 的对象
*/
async function exportKeyToJWK(key) {
return await crypto.subtle.exportKey("jwk", key);
}
/**
* 将 JWK 格式的 JSON 对象导入还原为可用的 AES-GCM 密钥
* @param {Object} jwkObject
* @returns {Promise<CryptoKey>}
*/
async function importKeyFromJWK(jwkObject) {
return await crypto.subtle.importKey(
"jwk",
jwkObject,
{ name: "AES-GCM" }, // 必须指定对应的算法
true, // 是否允许再次导出
["encrypt", "decrypt"] // 该密钥允许的用途
);
}
自 Node.js 19+ 起,Web Crypto API 已成为内置标准,可通过以下方式访问:
// 方式一:使用 globalThis
const { subtle } = globalThis.crypto;
// 方式二:使用 Node.js 模块
const { webcrypto } = require('node:crypto');
const { subtle } = webcrypto;
这两种方式虽然最终都能让你获取到 subtle 接口,但它们在设计哲学、模块机制、环境通用性以及底层依赖上有着本质的区别。
| 对比维度 | 方式一:globalThis.crypto | 方式二:require('node:crypto') |
|---|---|---|
| 本质定位 | Web标准API(与浏览器对齐) | Node.js原生模块API(特定服务端) |
| 引入方式 | 零依赖,直接全局访问 | 需要显式通过require引入模块 |
| 跨端通用性 | 极高(同构JavaScript) | 极低(只能在Node.js运行) |
| 底层实现 | 浏览器内核(前端)/ Node V8底层封装(后端) | 强依赖Node.js内置的OpenSSL库 |
| 执行上下文 | 必须在安全上下文(前端限制) | 服务端环境,无任何安全上下文限制 |
推荐使用"globalThis.crypto"的场景: 正在编写纯前端项目、前后端全栈通用工具库(Isomorphic Lib),或者代码需要部署在 Cloudflare Workers、Vercel Edge 等现代边缘计算架构中。
推荐使用"require('node:crypto')"的场景: 正在编写纯 Node.js 服务端项目(如大型后端服务、CLI 工具),且除了 Web Crypto 之外,还需要用到 Node.js 特有的高级安全特性(如流式加密大文件、硬件安全模块 HSM 交互等)。
Web Crypto API 是浏览器原生加密的黄金标准。它以零依赖、高性能、高安全性的优势,正在逐步取代各种第三方加密库。然而,正如其名 "subtle" 所警示的,加密是一个极其专业的领域,细微的错误就可能导致整个系统安全性完全失效。在使用 Web Crypto API 时:
以下为您编写的这一套 AES-GCM 高级对称加密与解密工具函数。它做到了100% 零依赖、纯原生,通过 globalThis.crypto 实现完美同构。你不需要进行任何环境判断,同一段代码可以无缝运行在浏览器(安全上下文)、Node.js(19+)、Deno、Bun 以及边缘计算(如 Cloudflare Workers)中。
为了解决数据传输与存储的问题,工具函数内部自动处理了 ArrayBuffer 与 Base64 字符串之间的相互转换。可以直接复制使用,代码如下:
/**
* 完美的同构 Web Crypto 安全加解密工具库 (TypeScript 箭头函数版本)
* 适用环境:Browser (HTTPS), Node.js 19+, Deno, Bun, Cloudflare Workers
*/
// 1. 获取全局同构的 subtle 对象
const cryptoProvider = globalThis.crypto
if (!cryptoProvider) {
throw new Error('当前运行环境不支持 Web Crypto API。请确保在 HTTPS、localhost 或现代 Node.js 19+ 环境下运行。')
}
const subtle: SubtleCrypto = cryptoProvider.subtle
/**
* 辅助函数:将 ArrayBuffer 转换为可传输的 Base64 字符串
* 使用现代批处理写法apply,100% 消除严格模式下的 undefined 类型报错
*/
const bufferToBase64 = (buffer: ArrayBuffer): string => {
const bytes = new Uint8Array(buffer)
const binary = String.fromCharCode.apply(null, bytes as unknown as number[])
return btoa(binary)
}
/**
* 辅助函数:将 Base64 字符串还原为 ArrayBuffer
* 使用 Array.from 映射,确保严格类型安全
*/
const base64ToBuffer = (base64: string): ArrayBuffer => {
const binaryString = atob(base64)
const bytes = Uint8Array.from(binaryString, (char) => char.charCodeAt(0))
return bytes.buffer
}
/**
* [同构] 生成 AES-GCM 256位加密密钥
*/
export const generateAESKey = async (): Promise<CryptoKey> => {
return await subtle.generateKey(
{ name: 'AES-GCM', length: 256 },
true, // 允许导出
['encrypt', 'decrypt']
)
}
/**
* [同构] 导出 CryptoKey 密钥为可传输的 Base64 字符串
* @param cryptoKey - 浏览器或服务端生成的 CryptoKey 实体
*/
export const exportKeyToBase64 = async (cryptoKey: CryptoKey): Promise<string> => {
const exported = await subtle.exportKey('raw', cryptoKey)
return bufferToBase64(exported)
}
/**
* [同构] 从 Base64 字符串导入还原为 CryptoKey 密钥对象
* @param base64Key - Base64 格式的密钥字符串
*/
export const importKeyFromBase64 = async (base64Key: string): Promise<CryptoKey> => {
const rawKeyBuffer = base64ToBuffer(base64Key)
return await subtle.importKey('raw', rawKeyBuffer, { name: 'AES-GCM' }, true, ['encrypt', 'decrypt'])
}
/**
* [同构] 使用 AES-GCM 算法加密字符串文本
* @param text - 待加密的明文文本
* @param key - CryptoKey 密钥对象
* @returns 返回打包了 "IV" 和 "密文" 的统一 Base64 字符串
*/
export const encryptText = async (text: string, key: CryptoKey): Promise<string> => {
const encoder = new TextEncoder()
const encodedText: Uint8Array<ArrayBuffer> = encoder.encode(text)
// 生成 12 字节的密码学安全伪随机数作为 IV (AES-GCM 标准推荐)
const iv: Uint8Array<ArrayBuffer> = cryptoProvider.getRandomValues(new Uint8Array(12))
// 执行加密,返回密文 ArrayBuffer
const cipherBuffer: ArrayBuffer = await subtle.encrypt({ name: 'AES-GCM', iv: iv }, key, encodedText)
// 将 IV(12字节)和密文拼接,方便单字符串传输
const combined = new Uint8Array(iv.length + cipherBuffer.byteLength)
combined.set(iv, 0)
combined.set(new Uint8Array(cipherBuffer), iv.length)
return bufferToBase64(combined.buffer)
}
/**
* [同构] 使用 AES-GCM 算法解密文本
* @param combinedBase64 - 由 encryptText 生成的统一加密 Base64 字符串(内部含 IV)
* @param key - 对应的 CryptoKey 密钥对象
*/
export const decryptText = async (combinedBase64: string, key: CryptoKey): Promise<string> => {
const combinedBuffer = base64ToBuffer(combinedBase64)
const combinedBytes = new Uint8Array(combinedBuffer)
// 从头部切出前 12 字节作为 IV
const iv: Uint8Array<ArrayBuffer> = combinedBytes.slice(0, 12)
// 剩下的部分作为真正的密文
const cipherText: Uint8Array<ArrayBuffer> = combinedBytes.slice(12)
// 执行解密
const decryptedBuffer: ArrayBuffer = await subtle.decrypt({ name: 'AES-GCM', iv: iv }, key, cipherText.buffer)
const decoder = new TextDecoder()
return decoder.decode(decryptedBuffer)
}
TS 测试脚本
如果你在 Vite/Nuxt/Next.js 项目或者使用了 ts-node / bun / deno 的后端环境中,可以直接引入并运行这段强类型的测试代码:
import { decryptText, encryptText, exportKeyToBase64, generateAESKey, importKeyFromBase64 } from 'crypto'
;(async () => {
try {
const rawData: string = '前后端全平台无缝传输的敏感数据 1234-abcd-TS-VERSION'
console.log('🔒 原始待加解密明文:', rawData)
// ==========================================
// 阶段一:在 A 端(例如 Node.js 后端)生成密钥并加密
// ==========================================
const originalKey: CryptoKey = await generateAESKey()
// 导出密钥为 Base64 字符串
const transportableKeyStr: string = await exportKeyToBase64(originalKey)
console.log('🔑 [传输中] 密钥已安全序列化为 Base64:', transportableKeyStr)
// 对数据进行加密
const secureCipherText: string = await encryptText(rawData, originalKey)
console.log('📦 [传输中] 密文已安全打包(含IV)为 Base64:', secureCipherText)
// ==========================================
// 阶段二:在 B 端(例如现代 Web 浏览器前端)收到数据解密
// ==========================================
console.log('\n--- 数据已通过网络无缝抵达另一端 ---')
// 1. 恢复强类型的 CryptoKey 实体
const receivedKey: CryptoKey = await importKeyFromBase64(transportableKeyStr)
// 2. 还原数据内容
const decryptedOutput: string = await decryptText(secureCipherText, receivedKey)
console.log('🔓 [还原成功] 解密后的最终明文结果:', decryptedOutput)
} catch (error) {
console.error('❌ 密码学同构解密链路失败,错误原因: ', error)
}
})()