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

Rijndael算法C# WPF加密解密文件保存失败及代码修复咨询

问题分析与修复方案

核心问题总结

  1. 对话框逻辑错误:加密/解密时先弹出保存对话框再配置过滤规则,且对话框顺序不合理(应先选源文件,再选目标文件)
  2. Rijndael密钥/IV不规范:直接用用户输入的密码作为Key和IV,不符合Rijndael对Key(16/24/32字节)、IV(16字节)的长度要求,且安全性极低
  3. CryptoStream未完成写入:加密时未调用FlushFinalBlock(),导致最后一段加密数据未写入文件
  4. 资源未正确释放:未使用using语句自动管理文件流、加密流资源,存在资源泄漏风险
  5. 异常处理过于模糊:无法定位具体错误原因

修复步骤

1. 修正对话框逻辑

  • 先弹出打开文件对话框,确认用户选择源文件后,再弹出保存文件对话框
  • 在ShowDialog()前设置对话框的Filter,确保过滤规则生效

2. 规范Rijndael的Key与IV处理

  • 使用密码派生Key(通过Rfc2898DeriveBytes),确保Key长度符合要求
  • 生成随机IV,加密时将IV写入输出文件开头,解密时先读取IV再解密

3. 完善加密流操作

  • 加密完成后调用CryptoStream.FlushFinalBlock()确保所有数据写入
  • 使用using语句自动释放所有流资源,替代手动Close()

4. 优化异常处理

  • 捕获具体异常并显示错误信息,便于调试

修改后的完整代码

private void ButtonEnc_Click(object sender, RoutedEventArgs e)
{
    var openFileDlg = new Microsoft.Win32.OpenFileDialog();
    if (openFileDlg.ShowDialog() != true)
        return;

    var saveFileDlg = new Microsoft.Win32.SaveFileDialog
    {
        Filter = "AES加密文件 (.aes)|*.aes"
    };
    if (saveFileDlg.ShowDialog() != true)
        return;

    string inputFile = openFileDlg.FileName;
    string outputFile = saveFileDlg.FileName;
    string password = TextBox1.Text;

    try
    {
        EncryptFile(inputFile, outputFile, password);
        MessageBox.Show("加密完成!", "提示");
    }
    catch (Exception ex)
    {
        MessageBox.Show($"加密失败:{ex.Message}", "错误");
    }
}

private void ButtonDec_Click_1(object sender, RoutedEventArgs e)
{
    var openFileDlg = new Microsoft.Win32.OpenFileDialog
    {
        Filter = "AES加密文件 (.aes)|*.aes",
        Multiselect = false
    };
    if (openFileDlg.ShowDialog() != true)
        return;

    var saveFileDlg = new Microsoft.Win32.SaveFileDialog();
    if (saveFileDlg.ShowDialog() != true)
        return;

    string inputFile = openFileDlg.FileName;
    string outputFile = saveFileDlg.FileName;
    string password = TextBox1.Text;

    try
    {
        DecryptFile(inputFile, outputFile, password);
        MessageBox.Show("解密完成!", "提示");
    }
    catch (Exception ex)
    {
        MessageBox.Show($"解密失败:{ex.Message}", "错误");
    }
}

private void EncryptFile(string inputFile, string outputFile, string password)
{
    // 生成随机盐值和IV
    byte[] salt = new byte[16];
    byte[] iv = new byte[16];
    using (var rng = new RNGCryptoServiceProvider())
    {
        rng.GetBytes(salt);
        rng.GetBytes(iv);
    }

    // 从密码派生符合要求的Key
    var deriveBytes = new Rfc2898DeriveBytes(password, salt, 10000);
    byte[] key = deriveBytes.GetBytes(32); // 256位Key

    using (var fsOut = new FileStream(outputFile, FileMode.Create))
    {
        // 先写入盐值和IV到文件开头,解密时需要读取
        fsOut.Write(salt, 0, salt.Length);
        fsOut.Write(iv, 0, iv.Length);

        using (var rijndael = new RijndaelManaged
        {
            Key = key,
            IV = iv,
            Mode = CipherMode.CBC, // 使用更安全的CBC模式
            Padding = PaddingMode.PKCS7
        })
        using (var encryptor = rijndael.CreateEncryptor())
        using (var cryptoStream = new CryptoStream(fsOut, encryptor, CryptoStreamMode.Write))
        using (var fsIn = new FileStream(inputFile, FileMode.Open))
        {
            // 批量复制数据,比逐字节读写效率更高
            fsIn.CopyTo(cryptoStream);
            cryptoStream.FlushFinalBlock();
        }
    }
}

private void DecryptFile(string inputFile, string outputFile, string password)
{
    using (var fsIn = new FileStream(inputFile, FileMode.Open))
    {
        // 读取文件开头的盐值和IV
        byte[] salt = new byte[16];
        byte[] iv = new byte[16];
        fsIn.Read(salt, 0, salt.Length);
        fsIn.Read(iv, 0, iv.Length);

        // 派生Key
        var deriveBytes = new Rfc2898DeriveBytes(password, salt, 10000);
        byte[] key = deriveBytes.GetBytes(32);

        using (var rijndael = new RijndaelManaged
        {
            Key = key,
            IV = iv,
            Mode = CipherMode.CBC,
            Padding = PaddingMode.PKCS7
        })
        using (var decryptor = rijndael.CreateDecryptor())
        using (var cryptoStream = new CryptoStream(fsIn, decryptor, CryptoStreamMode.Read))
        using (var fsOut = new FileStream(outputFile, FileMode.Create))
        {
            // 批量复制解密后的数据
            cryptoStream.CopyTo(fsOut);
        }
    }
}

额外说明

  • 使用Rfc2898DeriveBytes通过密码和盐值派生Key,比直接用密码转字节更安全,且能保证Key长度符合Rijndael要求
  • 随机生成IV并与加密数据一起存储,避免固定IV带来的安全风险
  • 采用CBC模式+PKCS7填充,符合标准加密规范
  • 使用CopyTo批量读写文件,比逐字节操作效率更高

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 12:55:35