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

密钥与填充配置正确但解密失败问题排查

解决RijndaelManaged解密时的“Padding is invalid and cannot be removed”错误

问题出在这几个地方:

  • IV长度解析错误:加密时你用BitConverter.GetBytes(iv.Length)把IV长度(int类型,占4字节)写入文件,但解密时却用BitConverter.ToUInt16(buffer, 0)解析——这会把4字节的整数截断成2字节,虽然当前你的IV长度刚好在UInt16范围内,逻辑上完全不匹配,一旦块大小调整就会直接出错。
  • 未确保完整读取数据:FileStream.Read()不会保证一次性读完你要的字节数,直接调用一次可能只读到部分数据,导致IV长度或IV本身不完整,用错误的IV解密肯定会触发填充错误。
  • 参数设置顺序不合理:构造函数里先设Key再设KeySize和BlockSize,虽然当前密钥长度刚好符合要求,但不符合最佳实践,容易引发潜在的参数不匹配问题。

修正后的完整代码

//Targeting .Net 4.5.2

//Key is hard coded, ONLY while working through this error in a minimum repo.
byte[] testKey = Convert.FromBase64String("KN1df3fOkLmSPyOP4r+grlVFDC/JVlWuew1u/hDGvUU=");

//Called to Encrypt ("D:\\Assets\\", "Test.txt")
public void DoWork(string filePath, string fileName)
{
    CryptographyUtil crypto = new CryptographyUtil(testKey);
    crypto.EncryptFile(filePath, fileName);
}

//Called to Decrypt ("D:\\Assets\\", "Test.txt.dat")
public void UnDoWork(string filePath, string fileName)
{
    CryptographyUtil crypto = new CryptographyUtil(testKey);
    crypto.DecryptFile(filePath, fileName);
}

public class CryptographyUtil
{
    RijndaelManaged rjndl;
    RNGCryptoServiceProvider cRng;
        
    public CryptographyUtil(byte[] key, int keySize = 256, int blockSize = 256)
    {
        cRng = new RNGCryptoServiceProvider();

        rjndl = new RijndaelManaged();
        // 先设置密钥长度和块大小,再赋值密钥,避免参数不匹配
        rjndl.KeySize = keySize;
        rjndl.BlockSize = blockSize;
        rjndl.Key = key;
        rjndl.Mode = CipherMode.CBC;
        rjndl.Padding = PaddingMode.PKCS7;
    }

    public void EncryptFile(string filePath, string fileName)
    {
        string inputFilePath = Path.Combine(filePath, fileName);
        if(!File.Exists(inputFilePath))
        {
            throw new FileLoadException("Unable to locate or open file.", inputFilePath);
        }
        
        string outputDirectory = Path.Combine(filePath, "Encrypted");
        
        if(!Directory.Exists(outputDirectory))
        {
            Directory.CreateDirectory(outputDirectory);
        }
        
        string outputPath = Path.Combine(outputDirectory, fileName + ".dat");
        
        //Create a unique IV each time
        byte[] iv = new byte[rjndl.BlockSize / 8];
        cRng.GetBytes(iv);
        byte[] ivSize = BitConverter.GetBytes(iv.Length);

        ICryptoTransform encryptor = rjndl.CreateEncryptor(rjndl.Key, iv);
        
        // using语句会自动释放资源,不用手动Close
        using(FileStream readStream = File.OpenRead(inputFilePath))
        using(FileStream writeStream = new FileStream(outputPath, FileMode.Create))
        using(CryptoStream encryptStream = new CryptoStream(writeStream, encryptor, CryptoStreamMode.Write))
        {
            // 先写IV长度和IV明文,再写加密数据
            writeStream.Write(ivSize, 0, ivSize.Length);
            writeStream.Write(iv, 0, iv.Length);

            readStream.CopyTo(encryptStream);
            encryptStream.FlushFinalBlock();
        }
    }

    public void DecryptFile(string filePath, string fileName)
    {
        string outputDirectory = Path.Combine(filePath, "Decrypted");
        
        if(!Directory.Exists(outputDirectory))
        {
            Directory.CreateDirectory(outputDirectory);
        }
        
        //Remove the ".dat" from the end of the file
        string outputFilePath = Path.Combine(outputDirectory, fileName.Substring(0, fileName.LastIndexOf('.')));
        
        if(File.Exists(outputFilePath))
        {
            File.Delete(outputFilePath);
        }

        using(FileStream readStream = File.OpenRead(Path.Combine(filePath, fileName)))
        {
            byte[] buffer = new byte[4];
            int bytesRead = 0;
            // 循环读取,确保拿到完整的4字节IV长度
            while(bytesRead < buffer.Length)
            {
                int read = readStream.Read(buffer, bytesRead, buffer.Length - bytesRead);
                if(read == 0)
                    throw new IOException("Unexpected end of file while reading IV length.");
                bytesRead += read;
            }
            // 用正确的Int32解析IV长度
            int ivLength = BitConverter.ToInt32(buffer, 0);

            buffer = new byte[ivLength];
            bytesRead = 0;
            // 同样循环读取,确保拿到完整的IV
            while(bytesRead < ivLength)
            {
                int read = readStream.Read(buffer, bytesRead, ivLength - bytesRead);
                if(read == 0)
                    throw new IOException("Unexpected end of file while reading IV.");
                bytesRead += read;
            }
        
            ICryptoTransform decryptor = rjndl.CreateDecryptor(rjndl.Key, buffer);

            buffer = new byte[1024];

            using(FileStream writeStream = new FileStream(outputFilePath, FileMode.Create))
            using(CryptoStream decryptStream = new CryptoStream(readStream, decryptor, CryptoStreamMode.Read))
            {
                int readIdx = decryptStream.Read(buffer, 0, buffer.Length);
                while(readIdx > 0)
                {
                    writeStream.Write(buffer, 0, readIdx);
                    readIdx = decryptStream.Read(buffer, 0, buffer.Length);
                }
            }
        }
    }       
}

顺便提几个优化点

  • 删掉了冗余的Flush()和Close():using语句会自动处理流的释放,手动调用纯属多余。
  • 加了读取完整性校验:避免因流读取不完整导致的IV错误,这是文件操作的基本规范。
  • 调整了Rijndael参数顺序:先设长度再设密钥,确保参数匹配,减少潜在bug。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 20:11:48