You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 09:29:51