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

原正常运行代码出现Padding相关CryptographicException问题求助

解决AES解密时的"Padding is invalid and cannot be removed"异常及数据恢复建议

我有一个存储加密文本数据的程序,新增数据时会从文件加载加密数据、解密、追加新字符串、重新加密后保存至磁盘。该程序此前运行正常,但近期解密加载的数据时抛出CryptographicException:Padding is invalid and cannot be removed。以下是我使用的加解密代码:

public static byte[] Encrypt(string text, byte[] key) {
    byte[] encrypted;
    byte[] IV;
    using (var aes = Aes.Create()) {
        aes.Key = key;
        aes.GenerateIV();
        IV = aes.IV;
        aes.Mode = CipherMode.CBC;
        var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
        using (var msEncrypt = new MemoryStream())
        using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) {
            using (var swEncrypt = new StreamWriter(csEncrypt)) {
                swEncrypt.Write(text);
            }
            encrypted = msEncrypt.ToArray();
        }
    }
    // return plaintext IV and encrypted text payload concatenated
    return IV.Concat(encrypted).ToArray();
} // Encrypt()

public static string Decrypt(byte[] bytes, byte[] key) {
    string plaintext = null;
    using (var aes = Aes.Create()) {
        aes.Key = key;
        // split iv and encryped cyphertext
        byte[] IV = new byte[aes.BlockSize / 8];
        byte[] cipherText = new byte[bytes.Length - IV.Length];
        Array.Copy(bytes, IV, IV.Length);
        Array.Copy(bytes, IV.Length, cipherText, 0, cipherText.Length);
        aes.IV = IV;
        aes.Mode = CipherMode.CBC;
        var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
        using (var msDecrypt = new MemoryStream(cipherText))
        using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
        using (var srDecrypt = new StreamReader(csDecrypt)) {
            plaintext = srDecrypt.ReadToEnd(); // !!! EXCEPTION HAPPENS HERE !!!
        }
    }
    return plaintext;
}

问题根源分析

你的代码存在两个核心问题:

  1. 加密流程未完成收尾操作:AES-CBC模式下,最后一块数据的PKCS7填充是在CryptoStream释放或调用FlushFinalBlock()时才会完成。你原加密代码中,encrypted = msEncrypt.ToArray()是在csEncrypt的using块内部执行的,此时csEncrypt还未释放,导致加密数据缺失最后一块的填充内容,解密时自然触发填充验证失败。
  2. 非原子性的文件写入:直接覆盖原文件的保存逻辑,一旦写入过程中出现中断(磁盘空间不足、程序崩溃),就会导致文件数据损坏,这也是你遇到异常的可能诱因。

代码修复方案

1. 修正加密方法

确保CryptoStream完成所有加密操作后再读取内存流数据,有两种实现方式:

方式1:将内存流读取移到CryptoStream的using块外部

当csEncrypt的using块结束时,会自动调用Dispose方法完成填充:

public static byte[] Encrypt(string text, byte[] key) {
    byte[] encrypted;
    byte[] IV;
    using (var aes = Aes.Create()) {
        aes.Key = key;
        aes.GenerateIV();
        IV = aes.IV;
        aes.Mode = CipherMode.CBC;
        var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
        using (var msEncrypt = new MemoryStream())
        {
            using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
            using (var swEncrypt = new StreamWriter(csEncrypt)) {
                swEncrypt.Write(text);
            } // csEncrypt在此处释放,完成填充操作
            encrypted = msEncrypt.ToArray();
        }
    }
    return IV.Concat(encrypted).ToArray();
}

方式2:手动调用FlushFinalBlock

在StreamWriter释放后,手动触发加密收尾:

public static byte[] Encrypt(string text, byte[] key) {
    byte[] encrypted;
    byte[] IV;
    using (var aes = Aes.Create()) {
        aes.Key = key;
        aes.GenerateIV();
        IV = aes.IV;
        aes.Mode = CipherMode.CBC;
        var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
        using (var msEncrypt = new MemoryStream())
        using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) {
            using (var swEncrypt = new StreamWriter(csEncrypt)) {
                swEncrypt.Write(text);
            }
            csEncrypt.FlushFinalBlock(); // 手动完成填充和加密收尾
            encrypted = msEncrypt.ToArray();
        }
    }
    return IV.Concat(encrypted).ToArray();
}

2. 改进文件保存逻辑(避免数据损坏)

采用原子写入方式,先写入临时文件,确认成功后再替换原文件:

// 示例保存逻辑
var tempFilePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
File.WriteAllBytes(tempFilePath, encryptedData);
// 原子替换原文件,确保写入失败时原文件不受影响
File.Move(tempFilePath, yourOriginalFilePath, overwrite: true);

数据恢复建议

根据你描述的解密结果(前1/3有效,后接乱码和重复16字节),数据大概率是部分损坏,可尝试以下步骤恢复:

  1. 立即备份损坏文件:复制原始损坏文件到安全位置,避免进一步破坏。
  2. 验证数据长度:正常加密数据总长度应为16(IV长度) + 16*N(密文长度,N为正整数),如果当前长度不符合,说明数据被截断或覆盖。
  3. 尝试截断密文到有效块长度:
    • 提取前16字节作为IV。
    • 截取后面的密文部分到最近的16字节整数倍长度(比如密文长度为X,取X - (X%16)字节,确保结果≥16)。
    • 用修正后的解密代码尝试解密截断后的密文,大概率能得到完整的前半部分有效文本,之后可手动补全丢失内容(若能回忆)。
  4. 查找备份:检查系统卷影副本(Windows)或个人备份文件,尝试恢复未损坏的历史版本。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 05:37:00