C# AES加密解密时MAC验证失败问题求助
问题分析与解决方案
核心错误
你的代码存在逻辑倒置:加密阶段对明文计算MAC,但解密阶段却对密文计算MAC,两者计算对象完全不同,必然导致MAC验证失败。
修复步骤
1. 修正加密阶段的MAC计算对象
加密时,MAC应计算在IV + 密文的组合数据上(确保解密时计算逻辑一致),而非明文。修改EncryptWithIntegrity方法:
public static byte[] EncryptWithIntegrity(string plaintext) { byte[] iv = GenerateRandomBytes(16); byte[] plaintextBytes = Encoding.UTF8.GetBytes(plaintext); using (Aes aesAlg = Aes.Create()) { aesAlg.Key = Encoding.UTF8.GetBytes(encryptionKey); aesAlg.IV = iv; ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); byte[] encryptedData; using (MemoryStream msEncrypt = new MemoryStream()) { using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) { csEncrypt.Write(plaintextBytes, 0, plaintextBytes.Length); } encryptedData = msEncrypt.ToArray(); } // 对IV+密文计算MAC,而非明文 byte[] combinedForMac = new byte[iv.Length + encryptedData.Length]; Buffer.BlockCopy(iv, 0, combinedForMac, 0, iv.Length); Buffer.BlockCopy(encryptedData, 0, combinedForMac, iv.Length, encryptedData.Length); byte[] mac = CalculateMAC(combinedForMac, encryptionKey); byte[] encryptedDataWithMAC = new byte[iv.Length + encryptedData.Length + mac.Length]; Buffer.BlockCopy(iv, 0, encryptedDataWithMAC, 0, iv.Length); Buffer.BlockCopy(encryptedData, 0, encryptedDataWithMAC, iv.Length, encryptedData.Length); Buffer.BlockCopy(mac, 0, encryptedDataWithMAC, iv.Length + encryptedData.Length, mac.Length); return encryptedDataWithMAC; } }
2. 修正解密阶段的MAC计算逻辑
解密时,使用接收到的IV和密文重新计算MAC,与传输过来的MAC对比:
public static string DecryptWithIntegrity(byte[] encryptedDataWithMAC) { int ivLength = 16; int macLength = 32; int expectedMinLength = ivLength + macLength; if (encryptedDataWithMAC == null || encryptedDataWithMAC.Length < expectedMinLength) { throw new ArgumentException("Invalid encrypted data format."); } byte[] iv = new byte[ivLength]; byte[] encryptedData = new byte[encryptedDataWithMAC.Length - ivLength - macLength]; byte[] receivedMAC = new byte[macLength]; Buffer.BlockCopy(encryptedDataWithMAC, 0, iv, 0, iv.Length); Buffer.BlockCopy(encryptedDataWithMAC, iv.Length, encryptedData, 0, encryptedData.Length); Buffer.BlockCopy(encryptedDataWithMAC, iv.Length + encryptedData.Length, receivedMAC, 0, receivedMAC.Length); // 对IV+密文计算MAC,与收到的MAC对比 byte[] combinedForMac = new byte[iv.Length + encryptedData.Length]; Buffer.BlockCopy(iv, 0, combinedForMac, 0, iv.Length); Buffer.BlockCopy(encryptedData, 0, combinedForMac, iv.Length, encryptedData.Length); byte[] calculatedMAC = CalculateMAC(combinedForMac, encryptionKey); if (!CompareMACs(receivedMAC, calculatedMAC)) { throw new CryptographicException("MAC verification failed. Data integrity compromised."); } using (Aes aesAlg = Aes.Create()) { aesAlg.Key = Encoding.UTF8.GetBytes(encryptionKey); aesAlg.IV = iv; ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); using (MemoryStream msDecrypt = new MemoryStream(encryptedData)) { using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) { using (StreamReader srDecrypt = new StreamReader(csDecrypt)) { return srDecrypt.ReadToEnd(); } } } } }
额外安全优化建议
- 密钥派生:直接将字符串转为AES密钥不安全,建议用PBKDF2从密码派生密钥,加入随机盐值:
private static byte[] DeriveKey(string password, byte[] salt) { using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000, HashAlgorithmName.SHA256)) { return pbkdf2.GetBytes(32); // 生成256位AES密钥 } } - 密钥分离:不要用同一密钥同时做AES加密和HMAC计算,应从主密钥派生出两个独立子密钥,避免复用风险。
- 使用内置认证加密算法:.NET提供
AesGcm/AesCcm这类内置认证加密算法,无需手动组合AES和HMAC,更安全且不易出错。
内容的提问来源于stack exchange,提问作者S1m1ng7on
相关产品推荐
相关产品推荐

