Angular与C#实现AES-GCM加解密报错排查及示例请求
AES-GCM 加解密认证标签不匹配问题排查及可运行代码
问题根源
你的代码存在以下关键错误,导致解密时认证标签验证失败:
- IV编码不一致:Angular端将生成的IV转为十六进制字符串,但C#端用Base64解析,导致IV字节完全不符。
- 附加数据未同步:加密时指定了
additionalData: 'nvn',但解密时未传入该数据——GCM模式下附加数据会参与认证计算,缺失必然导致标签验证失败。 - 变量逻辑混淆:C#端将拆分后的tag错误命名为
additionalString,易引发逻辑误解。 - 密钥长度验证缺失:AES-256-GCM要求密钥必须是32字节(256位),原代码未做长度校验。
修正后的可运行代码
Angular 加密代码(使用node-forge)
import * as forge from 'node-forge'; // 注意:密钥需为256位(32字节),示例用Base64编码格式,也可直接传入二进制缓冲区 const AES_KEY_BASE64 = '你的256位密钥Base64字符串'; // 比如:生成32字节随机数后转Base64 const FIXED_ADDITIONAL_DATA = 'nvn'; // 两端必须完全一致 function encryptAesGcm(data: string | object): string { // 生成12字节(96位)随机IV,符合GCM最佳实践 const ivBuffer = forge.random.getBytesSync(12); const ivBase64 = forge.util.encode64(ivBuffer); // 解码密钥为二进制缓冲区 const keyBuffer = forge.util.decode64(AES_KEY_BASE64); // 初始化GCM加密器 const cipher = forge.cipher.createCipher('AES-GCM', keyBuffer); cipher.start({ iv: ivBuffer, // 传入二进制缓冲区而非字符串 additionalData: FIXED_ADDITIONAL_DATA, tagLength: 128 }); // 处理明文数据 const plaintext = typeof data === 'object' ? JSON.stringify(data) : data.toString(); cipher.update(forge.util.createBuffer(plaintext, 'utf8')); cipher.finish(); // 转换密文和标签为Base64 const ciphertextBase64 = forge.util.encode64(cipher.output.data); const tagBase64 = forge.util.encode64(cipher.mode.tag.data); // 拼接传输内容:IV|标签|密文 return `${ivBase64}|${tagBase64}|${ciphertextBase64}`; } // 使用示例 const testData = "{hello world}"; const transmitMsg = encryptAesGcm(testData); console.log('加密后传输内容:', transmitMsg);
C# 解密代码(使用System.Cryptography)
using System; using System.Text; using System.Security.Cryptography; public class AesGcmDecryptor { private const string FIXED_ADDITIONAL_DATA = "nvn"; // 必须和Angular端一致 private const int AES_256_KEY_SIZE = 32; // 256位密钥对应32字节 public static string DecryptAesGcm(string cipherText, string keyBase64) { // 拆分传输内容 string[] parts = cipherText.Split('|'); if (parts.Length != 3) throw new ArgumentException("无效的加密内容格式"); string ivBase64 = parts[0]; string tagBase64 = parts[1]; string ciphertextBase64 = parts[2]; // 解码各部分为字节数组 byte[] keyBytes = Convert.FromBase64String(keyBase64); if (keyBytes.Length != AES_256_KEY_SIZE) throw new ArgumentException("密钥长度必须为32字节(AES-256)"); byte[] ivBytes = Convert.FromBase64String(ivBase64); byte[] tagBytes = Convert.FromBase64String(tagBase64); byte[] ciphertextBytes = Convert.FromBase64String(ciphertextBase64); // 初始化GCM解密器并解密 using var aesGcm = new AesGcm(keyBytes); byte[] plaintextBytes = new byte[ciphertextBytes.Length]; aesGcm.Decrypt(ivBytes, ciphertextBytes, tagBytes, plaintextBytes, Encoding.UTF8.GetBytes(FIXED_ADDITIONAL_DATA)); return Encoding.UTF8.GetString(plaintextBytes); } // 使用示例 public static void Main() { string keyBase64 = "你的256位密钥Base64字符串"; // 和Angular端一致 string cipherText = "从Angular端获取的传输内容"; string plaintext = DecryptAesGcm(cipherText, keyBase64); Console.WriteLine("解密结果: " + plaintext); } }
关键注意事项
- 密钥管理:禁止硬编码密钥,建议通过环境变量或安全配置中心传递。
- IV规则:GCM模式下IV必须随机且唯一(每次加密生成新IV),推荐使用12字节长度(符合NIST标准)。
- 附加数据一致性:如果不需要附加数据,两端都要设为
null或空字符串,不可一端设置一端忽略。 - 编码统一:所有二进制数据(IV、密钥、密文、标签)的Base64编码/解码逻辑必须两端完全一致。
内容的提问来源于stack exchange,提问作者rarumal
相关产品推荐
相关产品推荐

