如何实现Angular与C#兼容的AES-GCM加解密方法?
Angular AES-GCM加解密与C#后端兼容实现
我已经在Angular中实现了AES-GCM加密解密逻辑,现在需要编写C#后端代码与之兼容。以下是我的Angular实现、初始编写的C#解密代码,以及修正后的兼容版本。
Angular 加解密实现代码
async encryptStringAES256(key: string, xSalt: string, data: string): Promise<any> { return new Promise((resolve, reject) => { const salt = new Uint8Array(this.toUin8Arry(xSalt)); const decryptedData = new Uint8Array(this.stringToArray(data)); this.getKeyMaterial(key).then(async (res: any) => { const xkey = await window.crypto.subtle.deriveKey( { name: 'PBKDF2', iterations: 100000, salt, hash: 'SHA-256' }, res, { name: 'AES-GCM', length: 256}, true, ['encrypt', 'decrypt'] ); window.crypto.subtle.encrypt({ name: 'AES-GCM', iv: salt }, xkey, decryptedData).then( encrypted => { resolve(this.arrayToString(new Uint8Array(encrypted))); }) .catch(err => { // 异常处理 }); }); }); } async decryptStringAES256(key: string, xSalt: string, data: string): Promise<any> { const salt = new Uint8Array(this.toUin8Arry(xSalt)); const encryptedData = new Uint8Array(this.stringToArray(data)); this.getKeyMaterial(key).then(async (res: any) => { const xkey = await window.crypto.subtle.deriveKey( { name: 'PBKDF2', iterations: 100000, salt, hash: 'SHA-256' }, res, { name: 'AES-GCM', length: 256}, true, ['encrypt', 'decrypt'] ); window.crypto.subtle.decrypt({ name: 'AES-GCM', iv: salt }, xkey, encryptedData).then( decrypted => { const dec = this.arrayToString(new Uint8Array(decrypted)); resolve(dec); }) .catch(err => { reject(data); }); }); }); } getKeyMaterial(password: string): any { const enc = new TextEncoder(); return window.crypto.subtle.importKey( 'raw', enc.encode(password), 'PBKDF2', false, ['deriveBits', 'deriveKey'] ); }
初始编写的C#解密代码
public static void Decrypt(byte[] sourceData, string password) { try { var encryptedData = sourceData.AsSpan(); var key = Convert.FromBase64String(password).AsSpan(); var ivSizeBytes = 10; var tagSizeBytes = 0; var cipherSize = encryptedData.Length - tagSizeBytes - ivSizeBytes; var iv = encryptedData.Slice(0, ivSizeBytes); var cipherBytes = encryptedData.Slice(ivSizeBytes, cipherSize); var tagStart = ivSizeBytes + cipherSize; var tag = encryptedData.Slice(tagStart); using Rfc2898DeriveBytes pbkdf2 = new(Encoding.UTF8.GetBytes(password), iv.ToArray(), 100000, HashAlgorithmName.SHA256); Span<byte> plainBytes = cipherSize < 1024 ? stackalloc byte[cipherSize] : new byte[cipherSize]; using var aes = new AesGcm(pbkdf2.GetBytes(32)); aes.Decrypt(iv, cipherBytes, tag, plainBytes); var x = Encoding.UTF8.GetString(plainBytes); } catch (Exception e) { throw new Exception($"Fehler Decrypt: {e.Message}"); } }
修正后的C#兼容解密代码
初始代码存在几个关键问题:错误处理密码编码、从加密数据中截取IV(与Angular逻辑不符)、tag长度设为0。以下是修正后的版本:
using System.Security.Cryptography; using System.Text; public static string Decrypt(string encryptedDataStr, string password, string saltStr) { try { // 转换加密字符串为字节数组(需与Angular的arrayToString/stringToArray逻辑一致,这里假设是Base64) byte[] encryptedData = Convert.FromBase64String(encryptedDataStr); // 转换salt为字节数组(需匹配Angular的toUin8Arry实现,若为UTF-8则用以下代码,若为Base64则改用Convert.FromBase64String) byte[] salt = Encoding.UTF8.GetBytes(saltStr); // 用PBKDF2推导密钥,参数与Angular完全对齐 using Rfc2898DeriveBytes pbkdf2 = new(Encoding.UTF8.GetBytes(password), salt, 100000, HashAlgorithmName.SHA256); byte[] key = pbkdf2.GetBytes(32); // 生成256位AES密钥 // AES-GCM默认tag长度为16字节,Web Crypto返回的加密数据是「密文+tag」 int tagLength = 16; byte[] cipherText = encryptedData[..^tagLength]; byte[] tag = encryptedData[^tagLength..]; // 解密:Angular中直接用salt作为IV,因此这里传入salt作为IV using var aesGcm = new AesGcm(key); byte[] plainTextBytes = new byte[cipherText.Length]; aesGcm.Decrypt(salt, cipherText, tag, plainTextBytes); return Encoding.UTF8.GetString(plainTextBytes); } catch (Exception e) { throw new Exception($"解密错误: {e.Message}"); } }
核心兼容要点
- PBKDF2参数一致:迭代次数100000、哈希算法SHA-256、密钥长度32字节必须与Angular完全匹配
- IV与Salt共用:Angular中直接使用传入的salt作为AES-GCM的IV,后端需要前端传递该salt值,不能从加密数据中提取IV
- 加密数据结构:Web Crypto的
encrypt返回结果是「密文+16字节tag」,需从加密数据末尾截取tag - 编码统一:密码、salt的编码需与Angular保持一致(Angular使用
TextEncoder即UTF-8编码)
内容的提问来源于stack exchange,提问作者Enrico
相关产品推荐
相关产品推荐

