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

C# AES加密解密本地正常,VM加密内容无法被Azure应用解密

加密解密跨环境不兼容问题排查

问题场景

本地部署加密服务时功能正常,但将服务部署到虚拟机(VM)后,其加密内容无法被Azure上的应用解密。已确认加密密码、.NET版本一致。

加密代码

public static string EncryptToBase64(string content, byte[] password)
{
    if (string.IsNullOrEmpty(content))
        return content;

    byte[] inputBytes = Encoding.UTF8.GetBytes(content);
    string base64Encoded;

   
    using (Aes encryptor = Aes.Create("AES"))
    {                
        encryptor.KeySize = 256; // 32 * 8 == AES 256
        encryptor.BlockSize = 128; // 16 * 8
        encryptor.Mode = CipherMode.CBC;
        encryptor.Padding = PaddingMode.Zeros;

        encryptor.GenerateIV();

        var key = new Rfc2898DeriveBytes(password, saltBytes, 100);
        encryptor.Key = key.GetBytes(encryptor.KeySize / 8);
        

        using (MemoryStream ms = new MemoryStream())
        {
            using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
            {
                cs.Write(inputBytes, 0, inputBytes.Length);
                cs.FlushFinalBlock();

            }
            byte[] bt = ms.ToArray();
            // here we prepend the IV (16 bytes) to the cypher text
            var mixed = encryptor.IV.Concat(bt).ToArray();

            base64Encoded = Convert.ToBase64String(mixed);
        }             
        return base64Encoded;
    }
}

解密代码

public static string DecryptBase64(string base64content, byte[] password)
{
    if (string.IsNullOrEmpty(base64content))
        return base64content;

    using (Aes encryptor = Aes.Create("AES"))
    {
        encryptor.KeySize = 256; // 32 * 8 == AES 256
        encryptor.BlockSize = 128; // 16 * 8
        encryptor.Mode = CipherMode.CBC;
        encryptor.Padding = PaddingMode.Zeros;

        byte[] encyptedBytes = Convert.FromBase64String(base64content);

        var key = new Rfc2898DeriveBytes(password, saltBytes, 100);
        encryptor.Key = key.GetBytes(encryptor.KeySize / 8);
        encryptor.IV = encyptedBytes.Take(16).ToArray(); // pull first 16 bytes

        string plaintext = string.Empty;

        //int byteCount = 0;
        byte[] cipherBytes = encyptedBytes.Skip(16).ToArray(); // take remaining 
        byte[] plainTextBytes = new byte[cipherBytes.Length];

        using (MemoryStream ms = new MemoryStream(cipherBytes))
        {
            using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Read))
            {
                using (StreamReader srDecrypt = new StreamReader(cs))
                {
                    plaintext = srDecrypt.ReadToEnd();
                }
            }
        }

        return plaintext.TrimEnd('\0'); // zero padded so chuck out the extra \0 values
    }
}

可能的问题及排查方向

1. saltBytes 一致性问题

代码中使用了未定义的saltBytes变量,若VM与Azure环境的saltBytes值不同,Rfc2898DeriveBytes生成的加密密钥会完全不一致,直接导致解密失败。

  • 排查:在VM和Azure环境分别输出saltBytes的Base64编码,确认两者完全相同;若saltBytes是动态生成的,需将其与IV一起附加到密文头部,解密时先读取salt再生成密钥。

2. 密钥生成结果不一致

即使密码和salt一致,跨环境下Rfc2898DeriveBytes的实现可能存在隐性差异(虽.NET跨平台标准统一,但需验证)。

  • 排查:在VM和Azure环境中,用相同的password和saltBytes生成密钥,输出密钥的Base64编码,对比是否完全一致。

3. 编码与流读取问题

解密时使用StreamReader.ReadToEnd(),其默认编码可能因环境差异导致解码错误;而加密时明确使用Encoding.UTF8,两者不匹配会引发乱码或解密失败。

  • 排查:替换解密的流读取方式,直接读取字节数组再转字符串:
    using (MemoryStream ms = new MemoryStream(cipherBytes))
    {
        using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Read))
        {
            cs.Read(plainTextBytes, 0, plainTextBytes.Length);
        }
    }
    plaintext = Encoding.UTF8.GetString(plainTextBytes).TrimEnd('\0');
    

4. 填充模式的跨环境处理差异

PaddingMode.Zeros的实现虽为标准,但部分环境可能对末尾零字节的处理存在差异,导致解密后内容无法正确截断。

  • 排查:临时替换为PaddingMode.PKCS7(更安全的标准填充方式),测试跨环境解密是否正常;若正常,说明零填充的处理存在环境差异。

5. IV与密文的拼接/拆分错误

加密时将IV(16字节)与密文拼接,解密时通过Take(16)和Skip(16)拆分,若环境中字节数组的长度计算或处理存在异常,会导致IV或密文截取错误。

  • 排查:在VM中加密测试内容,输出完整加密字节数组的Base64、IV的Base64、密文的Base64;在Azure环境中手动设置IV和密文进行解密,验证是否能得到正确内容。

6. 异常信息定位

在Azure的解密代码中添加异常捕获,明确错误类型(如CryptographicException提示填充错误、密钥不匹配等),直接定位问题根源。

代码优化建议

  • 固定saltBytes或随密文传输,避免环境差异导致密钥不一致;
  • 将PaddingMode.Zeros替换为PaddingMode.PKCS7,提升安全性与兼容性;
  • 提高Rfc2898DeriveBytes的迭代次数(建议≥10000),增强密钥生成的抗暴力破解能力。

内容的提问来源于stack exchange,提问作者Juliyanage Silva

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 04:52:22