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

解密方法抛出"Padding is invalid and cannot be removed"异常的原因排查

解密时出现“Padding is invalid and cannot be removed”的问题排查与修复

我使用Rfc2898DeriveBytes和CryptoStream库对SQL连接字符串中的字符串值进行加解密操作,但在解密加密后的值时,出现如下错误:

Padding is invalid and cannot be removed

我查阅了若干可能的解决方案,多数都提到要指定显式填充值,而我已经设置了该值。相关代码如下:

var t1 = "my_sql_password";
var t2 = Encrypt(t1, encryptionKey);
var t3 = Decrypt(t2, encryptionKey); // 抛出"Padding is invalid and cannot be removed."错误

public static string Encrypt(string stringToEncrypt, string encryptionKey)
{
    byte[] clearBytes = Encoding.Unicode.GetBytes(stringToEncrypt);
    using (Aes encryptor = Aes.Create())
    {
        Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(encryptionKey, 7, 5, HashAlgorithmName.SHA1);
        encryptor.Key = pdb.GetBytes(32);
        encryptor.IV = pdb.GetBytes(16);
        encryptor.Padding = PaddingMode.PKCS7;
        using (MemoryStream ms = new MemoryStream())
        {
            using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
            {
                cs.Write(clearBytes, 0, clearBytes.Length);
                cs.Close();
            }
            stringToEncrypt = Convert.ToBase64String(ms.ToArray());
        }
    }
    return stringToEncrypt;
}

public static string Decrypt(string stringToDecrypt, string encryptionKey)
{
    stringToDecrypt = stringToDecrypt.Replace(" ", "+");
    byte[] cipherBytes = Convert.FromBase64String(stringToDecrypt);
    using (Aes encryptor = Aes.Create())
    {
        Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(encryptionKey, 7, 5, HashAlgorithmName.SHA1);
        encryptor.Key = pdb.GetBytes(32);
        encryptor.IV = pdb.GetBytes(16);
        encryptor.Padding = PaddingMode.PKCS7;
        using (MemoryStream ms = new MemoryStream())
        {
            using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
            {
                cs.Write(cipherBytes, 0, cipherBytes.Length);
                cs.Close(); // *** 此处抛出"Padding is invalid and cannot be removed." ***
            }
            stringToDecrypt = Encoding.Unicode.GetString(ms.ToArray());
        }
    }
    return stringToDecrypt;
}

问题根源

代码里有两个核心错误导致解密失败:

  1. 盐参数使用错误:Rfc2898DeriveBytes的第二个参数是盐(salt)的字节数组,你传的整数7会被隐式转成单字节数组[0x07]——虽然语法合法,但盐必须保证加密和解密完全一致,且硬编码固定盐也不符合安全规范,容易导致密钥推导不一致。
  2. CryptoStream未正确收尾:直接调用cs.Close()无法保证加密/解密过程中所有数据(包括填充数据)都被正确处理,必须调用cs.FlushFinalBlock()完成流的收尾操作,否则解密时无法正确验证填充格式。

修复后的代码

正确的做法是:加密时生成随机盐,将盐与密文一起存储;解密时先提取盐,再用相同的盐推导密钥和IV,同时确保CryptoStream正确收尾。

var t1 = "my_sql_password";
var t2 = Encrypt(t1, encryptionKey);
var t3 = Decrypt(t2, encryptionKey); // 正常解密

public static string Encrypt(string stringToEncrypt, string encryptionKey)
{
    byte[] clearBytes = Encoding.Unicode.GetBytes(stringToEncrypt);
    // 生成16字节随机盐,提升加密安全性
    byte[] salt = new byte[16];
    using (var rng = RandomNumberGenerator.Create())
    {
        rng.GetBytes(salt);
    }

    using (Aes encryptor = Aes.Create())
    {
        // 用随机盐推导Key和IV
        Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(encryptionKey, salt, 5, HashAlgorithmName.SHA1);
        encryptor.Key = pdb.GetBytes(32);
        encryptor.IV = pdb.GetBytes(16);
        encryptor.Padding = PaddingMode.PKCS7;

        using (MemoryStream ms = new MemoryStream())
        {
            // 先写入盐,方便解密时提取
            ms.Write(salt, 0, salt.Length);
            
            using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
            {
                cs.Write(clearBytes, 0, clearBytes.Length);
                cs.FlushFinalBlock(); // 替代Close,确保所有加密数据处理完成
            }
            
            return Convert.ToBase64String(ms.ToArray());
        }
    }
}

public static string Decrypt(string stringToDecrypt, string encryptionKey)
{
    stringToDecrypt = stringToDecrypt.Replace(" ", "+");
    byte[] allBytes = Convert.FromBase64String(stringToDecrypt);
    
    // 提取前16字节作为盐
    byte[] salt = new byte[16];
    Array.Copy(allBytes, 0, salt, 0, salt.Length);
    // 提取剩余部分作为密文
    byte[] cipherBytes = new byte[allBytes.Length - salt.Length];
    Array.Copy(allBytes, salt.Length, cipherBytes, 0, cipherBytes.Length);

    using (Aes encryptor = Aes.Create())
    {
        // 用相同的盐推导Key和IV
        Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(encryptionKey, salt, 5, HashAlgorithmName.SHA1);
        encryptor.Key = pdb.GetBytes(32);
        encryptor.IV = pdb.GetBytes(16);
        encryptor.Padding = PaddingMode.PKCS7;

        using (MemoryStream ms = new MemoryStream())
        {
            using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
            {
                cs.Write(cipherBytes, 0, cipherBytes.Length);
                cs.FlushFinalBlock(); // 确保解密完成,验证填充格式
            }
            
            return Encoding.Unicode.GetString(ms.ToArray());
        }
    }
}

额外说明

  • 盐必须随机生成,每个加密内容使用不同的盐,避免相同明文生成相同密文,提升抗破解能力。
  • Rfc2898DeriveBytes的迭代次数(当前为5)建议设置为10000以上,增强密钥推导的安全性。
  • 密钥不要硬编码,应从安全配置存储(如Windows凭据管理器、云密钥管理服务)中读取。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 00:47:11