如何同步实现AES-GCM加解密?Web Crypto API及替代方案咨询
解决方案:AES-GCM同步加密适配Angular应用
一、使用现有crypto-js实现同步AES-GCM加解密
你当前的Angular应用已经在使用crypto-js,实际上该库原生支持AES-GCM模式,无需更换新库,只需调整加密配置即可,这是改动最小的方案。
示例代码
加密(同步)
import * as CryptoJS from 'crypto-js'; function encryptAesGcm(data: string, key: string): string { // 生成随机IV(AES-GCM要求IV长度为12字节,推荐值) const iv = CryptoJS.lib.WordArray.random(12); // 执行加密,指定模式为GCM const encrypted = CryptoJS.AES.encrypt(data, key, { mode: CryptoJS.mode.GCM, iv: iv, padding: CryptoJS.pad.NoPadding // GCM模式无需填充 }); // 将IV和密文拼接后返回(解密时需要用到IV) return iv.toString(CryptoJS.enc.Base64) + ':' + encrypted.toString(); }
解密(同步)
function decryptAesGcm(encryptedData: string, key: string): string { // 拆分IV和密文 const [ivBase64, ciphertext] = encryptedData.split(':'); const iv = CryptoJS.enc.Base64.parse(ivBase64); // 执行解密 const decrypted = CryptoJS.AES.decrypt(ciphertext, key, { mode: CryptoJS.mode.GCM, iv: iv, padding: CryptoJS.pad.NoPadding }); return decrypted.toString(CryptoJS.enc.Utf8); }
这种方式完全沿用你现有代码的调用风格,仅需替换原AES-CBC的配置,几乎不需要修改业务调用逻辑。
二、将Web Crypto API封装为“类同步”调用方式
Web Crypto API本身是异步的,但可以通过封装让调用逻辑更贴近原有同步代码的风格,最小化改动量。
封装思路
- 把Web Crypto的异步操作封装到Angular服务中,对外暴露类似同步的方法(实际是返回Promise,用
async/await调用) - 统一处理密钥、数据的格式转换(比如字符串与ArrayBuffer的互转),让调用方无需关心底层细节
示例封装服务
import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class CryptoService { // 将字符串转为ArrayBuffer private stringToArrayBuffer(str: string): ArrayBuffer { return new TextEncoder().encode(str); } // 将ArrayBuffer转为Base64字符串 private arrayBufferToBase64(buf: ArrayBuffer): string { return btoa(String.fromCharCode(...new Uint8Array(buf))); } // 将Base64字符串转为ArrayBuffer private base64ToArrayBuffer(base64: string): ArrayBuffer { const binary = atob(base64); const buf = new ArrayBuffer(binary.length); const view = new Uint8Array(buf); for (let i = 0; i < binary.length; i++) { view[i] = binary.charCodeAt(i); } return buf; } // 生成AES密钥(从字符串密钥转换) private async getKeyFromRawString(key: string): Promise<CryptoKey> { const keyBuffer = this.stringToArrayBuffer(key); return crypto.subtle.importKey( 'raw', keyBuffer, { name: 'AES-GCM' }, false, ['encrypt', 'decrypt'] ); } // 加密方法(对外暴露,调用时用await) async encrypt(data: string, key: string): Promise<string> { const cryptoKey = await this.getKeyFromRawString(key); const iv = crypto.getRandomValues(new Uint8Array(12)); // 12字节IV const dataBuffer = this.stringToArrayBuffer(data); const encryptedBuffer = await crypto.subtle.encrypt( { name: 'AES-GCM', iv: iv }, cryptoKey, dataBuffer ); // 拼接IV和密文(Base64格式) const ivBase64 = this.arrayBufferToBase64(iv); const ciphertextBase64 = this.arrayBufferToBase64(encryptedBuffer); return `${ivBase64}:${ciphertextBase64}`; } // 解密方法(对外暴露,调用时用await) async decrypt(encryptedData: string, key: string): Promise<string> { const [ivBase64, ciphertextBase64] = encryptedData.split(':'); const iv = this.base64ToArrayBuffer(ivBase64); const ciphertextBuffer = this.base64ToArrayBuffer(ciphertextBase64); const cryptoKey = await this.getKeyFromRawString(key); const decryptedBuffer = await crypto.subtle.decrypt( { name: 'AES-GCM', iv: iv }, cryptoKey, ciphertextBuffer ); return new TextDecoder().decode(decryptedBuffer); } }
业务代码调用示例
原来的同步调用代码:
const encrypted = CryptoJS.AES.encrypt(data, key, cbcOptions).toString();
现在只需改为:
// 在async函数中调用 const encrypted = await this.cryptoService.encrypt(data, key);
如果原有代码在非异步上下文(比如组件的构造函数),可以用IIFE包裹:
(async () => { const encrypted = await this.cryptoService.encrypt(data, key); // 后续业务逻辑 })();
这种方式仅需修改加密解密的调用处,业务逻辑基本无需调整,能最大程度保证应用稳定性。
内容的提问来源于stack exchange,提问作者user3860630
相关产品推荐
相关产品推荐

