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

C#中AES-CBC加密解密如何复用密钥与IV?填充异常排查

解决Rijndael加密解密的密钥/IV复用问题

兄弟,我看你这代码里踩了两个关键小坑,导致解密时拿不到正确的密钥和IV,还触发了Padding错误——咱们一步步理清楚:

首先说你当前的问题根源

  1. 你在加密方法里生成了Key和IV,但只是转成字符串就丢了,没把它们和密文一起返回/保存,解密时自然拿不到正确的密钥,用其他密钥解密肯定失败;
  2. 原代码里还有个低级错误:你在using里实例化了myRijndaelManaged,但加密时却用了外面的myRijndael的Key和IV,这完全是两个不同的实例,等于用了未初始化的密钥,这也会导致解密失败。

那个Padding is invalid and cannot be removed异常是正常的:当密钥/IV不对时,解密出来的字节流是乱码,PKCS7填充验证不通过(PKCS7要求填充的是“缺失的字节数”,比如缺3个字节就填充3个0x03),所以就会抛出这个异常,这是加密库的安全机制,防止攻击者试探解密是否成功。

正确的解决方案:加密时把密文+密钥+IV一起返回

要复用密钥和IV,必须在加密时把它们和密文绑定在一起,比如返回一个包含这三个信息的自定义对象,或者用分隔符拼接成字符串(注意分隔符要选不会和密文/密钥冲突的)。

第一步:定义一个加密结果类(方便返回多值)

public class EncryptionResult
{
    // 加密后的密文(Base64格式)
    public string CipherText { get; set; }
    // 生成的密钥(十六进制字符串)
    public string Key { get; set; }
    // 生成的初始向量(十六进制字符串)
    public string IV { get; set; }
}

第二步:修正你的EncryptText方法

public EncryptionResult EncryptText(string plainText) 
{ 
    // 注意这里直接用using里的实例,不要搞两个Rijndael实例
    using (var myRijndaelManaged = new RijndaelManaged()) 
    { 
        myRijndaelManaged.Mode = CipherMode.CBC; 
        myRijndaelManaged.Padding = PaddingMode.PKCS7; 
        // 生成随机IV和密钥
        myRijndaelManaged.GenerateIV(); 
        myRijndaelManaged.GenerateKey(); 
        // 转成十六进制字符串方便存储/传输
        string keyHex = ByteArrayToHexString(myRijndaelManaged.Key); 
        string ivHex = ByteArrayToHexString(myRijndaelManaged.IV); 
        // 这里一定要用当前实例的Key和IV,别再搞错对象了!
        byte[] encryptedBytes = EncryptStringToBytes(plainText, myRijndaelManaged.Key, myRijndaelManaged.IV); 
        string cipherText = Convert.ToBase64String(encryptedBytes); 
        // 返回包含所有必要信息的对象
        return new EncryptionResult 
        {
            CipherText = cipherText,
            Key = keyHex,
            IV = ivHex
        };
    } 
}

第三步:编写DecryptText方法,复用加密时的Key和IV

首先你需要一个ByteArrayToHexString的逆方法HexStringToByteArray:

public static byte[] HexStringToByteArray(string hex) 
{
    int length = hex.Length;
    byte[] bytes = new byte[length / 2];
    for (int i = 0; i < length; i += 2) 
    {
        bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
    }
    return bytes;
}

然后解密方法:

public string DecryptText(string cipherText, string keyHex, string ivHex) 
{
    using (var myRijndaelManaged = new RijndaelManaged()) 
    {
        myRijndaelManaged.Mode = CipherMode.CBC;
        myRijndaelManaged.Padding = PaddingMode.PKCS7;
        // 把十六进制的Key和IV转回字节数组
        byte[] key = HexStringToByteArray(keyHex);
        byte[] iv = HexStringToByteArray(ivHex);
        // 把Base64密文转成字节数组
        byte[] cipherBytes = Convert.FromBase64String(cipherText);
        // 解密成明文字节数组,再转成字符串(注意编码要和加密时一致,比如UTF8)
        byte[] plainBytes = DecryptStringFromBytes(cipherBytes, key, iv);
        return Encoding.UTF8.GetString(plainBytes);
    }
}

额外小建议

  • 如果你不想每次都生成新密钥,可以预先固定一个密钥,但IV必须每次加密都随机生成(CBC模式下重复IV会导致相同明文加密后出现相同密文,泄露信息);
  • 固定密钥的话,绝对不要硬编码在代码里,要存在安全的地方,比如Windows凭据管理器、密钥管理服务(KMS)等;
  • 确保你的EncryptStringToBytes和DecryptStringFromBytes方法里用的编码一致(比如都是UTF8),否则也会出现解密乱码的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:18:34