BouncyCastle C# AES GCM解密求助:遇密钥长度与MAC校验错误
AES GCM加密解密问题修正(C# BouncyCastle)
原代码核心问题
- 加密时每次生成新密钥,但解密未使用该密钥,反而错误将IV当作密钥传入,导致「密钥长度不合法」错误。
- 解密未正确解析Base64密文,直接将字符串转UTF8字节,而非用
Convert.FromBase64String解码。 - 加密与解密使用的Nonce不一致(解密时用空数组),导致「GCM MAC校验失败」。
- 解密时未传入与加密一致的关联数据,GCM参数初始化不完整。
- 加密生成的密钥未保留,无法用于解密。
修正后的完整代码
加密函数
using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; using System.Text; public static string AesGcmEncrypt(string plainText, byte[] key, byte[] nonce, string associatedData = "") { var plainBytes = Encoding.UTF8.GetBytes(plainText); var adBytes = Encoding.UTF8.GetBytes(associatedData); var gcm = new GcmBlockCipher(new AesEngine()); var parameters = new AeadParameters(new KeyParameter(key), 128, nonce, adBytes); gcm.Init(true, parameters); var outputLength = gcm.GetOutputSize(plainBytes.Length); var cipherBytes = new byte[outputLength]; var processed = gcm.ProcessBytes(plainBytes, 0, plainBytes.Length, cipherBytes, 0); gcm.DoFinal(cipherBytes, processed); // 将Nonce与密文+Tag拼接后转Base64,方便解密拆分 var combined = new byte[nonce.Length + cipherBytes.Length]; Buffer.BlockCopy(nonce, 0, combined, 0, nonce.Length); Buffer.BlockCopy(cipherBytes, 0, combined, nonce.Length, cipherBytes.Length); return Convert.ToBase64String(combined); }
解密函数
public static string AesGcmDecrypt(string encryptedBase64, byte[] key, string associatedData = "") { var combinedBytes = Convert.FromBase64String(encryptedBase64); // 拆分12字节Nonce(GCM标准推荐长度)和密文+Tag const int nonceLength = 12; var nonce = new byte[nonceLength]; var cipherBytes = new byte[combinedBytes.Length - nonceLength]; Buffer.BlockCopy(combinedBytes, 0, nonce, 0, nonceLength); Buffer.BlockCopy(combinedBytes, nonceLength, cipherBytes, 0, cipherBytes.Length); var adBytes = Encoding.UTF8.GetBytes(associatedData); var gcm = new GcmBlockCipher(new AesEngine()); var parameters = new AeadParameters(new KeyParameter(key), 128, nonce, adBytes); gcm.Init(false, parameters); var outputLength = gcm.GetOutputSize(cipherBytes.Length); var plainBytes = new byte[outputLength]; var processed = gcm.ProcessBytes(cipherBytes, 0, cipherBytes.Length, plainBytes, 0); gcm.DoFinal(plainBytes, processed); return Encoding.UTF8.GetString(plainBytes); }
使用示例
public static void TestAesGcm() { // 生成128位AES密钥(16字节) var keyGen = new CipherKeyGenerator(); keyGen.Init(new KeyGenerationParameters(new SecureRandom(), 128)); var key = ((KeyParameter)keyGen.GenerateKeyParameter()).GetKey(); // 生成12字节Nonce(GCM标准推荐,需保证每次加密唯一) var nonce = new byte[12]; new SecureRandom().NextBytes(nonce); string plainText = "Hello 123"; string encrypted = AesGcmEncrypt(plainText, key, nonce); Console.WriteLine($"加密结果:{encrypted}"); string decrypted = AesGcmDecrypt(encrypted, key); Console.WriteLine($"解密结果:{decrypted}"); }
关键修正说明
- 密钥管理:加密生成的密钥必须在解密方安全共享,示例中生成128位合规密钥,避免用IV代替密钥。
- Nonce规范:使用GCM推荐的12字节Nonce,加密时将Nonce与密文拼接,解密时拆分,确保两端Nonce一致。
- Base64处理:加密后将二进制数据转Base64,解密时反向解析,避免字符编码错误。
- GCM参数一致性:加密和解密使用相同的Tag长度(128位)、关联数据,保证MAC校验通过。
内容的提问来源于stack exchange,提问作者DemarcPoint
相关产品推荐
相关产品推荐

