C# AES-GCM加密解密异常:解密失败报参数越界求助
解决AES-GCM加密解密不匹配的问题
在C#中使用AES-GCM实现加密解密时,现有加密代码生成的密文无法被原解密代码解析,运行时抛出错误:Specified argument was out of the range of valid values。要求保留现有解密代码不变,修改加密代码修复问题。
错误原因
原加密代码的核心问题是没有将AES-GCM生成的认证标签(tag)写入到最终输出的密文中,但解密代码会从密文的最后16字节读取tag,导致解密时无法获取合法的tag,触发参数越界错误。
修改后的加密代码(保留原解密代码不变)
仅调整加密逻辑,其余代码保持原样:
// Encryption code. private static byte[] salt = new byte[16]; private static byte[] iv = new byte[12]; private const int iterations = 100000; public string Encrypt(string key, string data) { byte[] tag = new byte[16]; using (var rng = RandomNumberGenerator.Create()) { rng.GetBytes(salt); rng.GetBytes(iv); } byte[] dataBytes = Encoding.UTF8.GetBytes(data); byte[] privateKey = DeriveKey(key); using (AesGcm aes = new AesGcm(privateKey, tag.Length)) { byte[] encrypted = new byte[dataBytes.Length]; aes.Encrypt(iv, dataBytes, encrypted, tag); // 修复点1:扩展缓冲区长度,加入tag的16字节容量 byte[] combinedBuffer = new byte[salt.Length + iv.Length + encrypted.Length + tag.Length]; Buffer.BlockCopy(salt, 0, combinedBuffer, 0, salt.Length); Buffer.BlockCopy(iv, 0, combinedBuffer, salt.Length, iv.Length); Buffer.BlockCopy(encrypted, 0, combinedBuffer, salt.Length + iv.Length, encrypted.Length); // 修复点2:将tag写入缓冲区末尾,与解密代码的读取逻辑对齐 Buffer.BlockCopy(tag, 0, combinedBuffer, salt.Length + iv.Length + encrypted.Length, tag.Length); string base64Buff = BuffToBase64(combinedBuffer); return base64Buff; } } // 以下方法保持不变 public static byte[] DeriveKey(string password) { byte[] passwordBytes = Encoding.UTF8.GetBytes(password); using (var pbkdf2 = new Rfc2898DeriveBytes(passwordBytes, salt, iterations, HashAlgorithmName.SHA256)) { byte[] key = pbkdf2.GetBytes(32); return key; } } public static string BuffToBase64(byte[] buff) { return Convert.ToBase64String(buff); } public static byte[] Base64ToBuff(string b64) { return Convert.FromBase64String(b64); } // Decryption code 保持不变 public string Decrypt(string password, string encryptedString) { Span<byte> encryptedData = Convert.FromBase64String(encryptedString).AsSpan(); Span<byte> salt = encryptedData[..16]; Span<byte> nonce = encryptedData[16..(16 + 12)]; Span<byte> data = encryptedData[(16 + 12)..^16]; Span<byte> tag = encryptedData[^16..]; using Rfc2898DeriveBytes pbkdf2 = new(Encoding.UTF8.GetBytes(password), salt.ToArray(), 100000, HashAlgorithmName.SHA256); using AesGcm aes = new(pbkdf2.GetBytes(32), tag.Length); Span<byte> result = new byte[data.Length]; aes.Decrypt(nonce, data, tag, result); return Encoding.UTF8.GetString(result); } // 测试代码 var r = new AesGcmEncryption().Encrypt("my passphrase", "abcd"); var r2 = new AesGcmEncryption().Decrypt("my passphrase", r); Console.WriteLine(r); Console.WriteLine(r2);
修改关键点说明
- 扩展
combinedBuffer的长度,增加tag的16字节容量,确保能容纳salt、iv、密文、tag四部分数据 - 新增tag的写入逻辑,将AES-GCM生成的认证标签放到缓冲区末尾,完全匹配解密代码从密文最后16字节读取tag的逻辑
额外提示(非必须修改)
原代码中salt和iv使用静态字段,多线程并发加密时可能出现数据干扰问题,建议改为方法内的局部变量,避免不同加密请求互相影响。该修改不影响解密兼容性,可根据实际场景优化。
内容的提问来源于stack exchange,提问作者TOAN-VN
相关产品推荐
相关产品推荐

