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

C#客户端-服务器端含图片加密Word文档收发异常求助

Word文档加密收发问题:含图片文件解密失败

我正在开发一个带Word文档端到端加密的客户端-服务器演示程序,无图片的Word文档可正常收发,但含图片的文档无法处理。加解密函数应该没问题(无图片文件能正常加解密),问题大概率出在收发函数本身——点击接收按钮时会提示padding is invalid and cannot be removed(填充无效且无法移除)。

相关代码

发送文件函数

private void SendFileButton_Click(object sender, RoutedEventArgs e)
{
    Microsoft.Win32.OpenFileDialog openFileDialog = new Microsoft.Win32.OpenFileDialog
    {
        Filter = "Word Documents|*.docx"
    };

    if (openFileDialog.ShowDialog() == true)
    {
        string filePath = openFileDialog.FileName;

        byte[] encryptedData = EncryptData(filePath);

        stream.Write(encryptedData, 0, encryptedData.Length);
        MessageBox.Show("File sent!");
    }
}

接收文件函数

private void ReceiveFileButton_Click(object sender, RoutedEventArgs e)
{
    byte[] buffer = new byte[client.ReceiveBufferSize];
    int bytesRead = stream.Read(buffer, 0, buffer.Length);
    byte[] encryptedData = new byte[bytesRead];
    Array.Copy(buffer, encryptedData, bytesRead);

    byte[] decryptedData = DecryptData(encryptedData);

    Microsoft.Win32.SaveFileDialog saveFileDialog = new Microsoft.Win32.SaveFileDialog
    {
        Filter = "Word Documents|*.docx"
    };

    if (saveFileDialog.ShowDialog() == true)
    {
        string filePath = saveFileDialog.FileName;
        File.WriteAllBytes(filePath, decryptedData);
        MessageBox.Show("File received and saved!");
    }
}

加解密函数

private byte[] EncryptData(string inputFilePath)
{
    byte[] documentBytes;
    using (MemoryStream memoryStream = new MemoryStream())
    {
        using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(inputFilePath, false))
        {
            wordDoc.Clone(memoryStream);
        }
        documentBytes= memoryStream.ToArray();
    }

    byte[] encryptedData;
    using (Aes aes = Aes.Create())
    {
        aes.Key = aesKey;
        aes.GenerateIV();
        using (MemoryStream memoryStream = new MemoryStream())
        {
            memoryStream.Write(aes.IV, 0, aes.IV.Length);
            using (CryptoStream cryptoStream = new CryptoStream(memoryStream, aes.CreateEncryptor(), CryptoStreamMode.Write))
            {
                cryptoStream.Write(documentBytes, 0, documentBytes.Length);
            }
            encryptedData = memoryStream.ToArray();
            return encryptedData;
        }
    }
}
private byte[] DecryptData(byte[] data)
{
    byte[] decryptedData;
    using (Aes aes = Aes.Create())
    {
        aes.Key = aesKey;
        byte[] iv = new byte[aes.BlockSize / 8];
        Array.Copy(data, 0, iv, 0, iv.Length); 
        aes.IV = iv;

        using (MemoryStream memoryStream = new MemoryStream(data, iv.Length, data.Length - iv.Length))
        using (CryptoStream cryptoStream = new CryptoStream(memoryStream, aes.CreateDecryptor(), CryptoStreamMode.Read))
        using (MemoryStream decryptedStream = new MemoryStream())
        {
            cryptoStream.CopyTo(decryptedStream);
            decryptedData = decryptedStream.ToArray();
            return decryptedData;
        }
    }
}

问题原因

TCP是流式传输,没有固定数据边界。接收函数只调用了一次stream.Read(),当加密后的文件数据(含图片的文档通常更大)超过client.ReceiveBufferSize时,只能读取到部分数据。不完整的加密数据在解密时,会触发AES的填充验证失败,也就是你看到的错误。无图片的小文件刚好能一次读完,所以不会出问题。

解决方案

需要先发送加密数据的长度,接收端先读取长度,再循环读取所有数据,确保拿到完整的加密内容:

修改后的发送函数

private void SendFileButton_Click(object sender, RoutedEventArgs e)
{
    Microsoft.Win32.OpenFileDialog openFileDialog = new Microsoft.Win32.OpenFileDialog
    {
        Filter = "Word Documents|*.docx"
    };

    if (openFileDialog.ShowDialog() == true)
    {
        string filePath = openFileDialog.FileName;
        byte[] encryptedData = EncryptData(filePath);

        // 先发送数据长度(用4字节int存储)
        byte[] lengthBytes = BitConverter.GetBytes(encryptedData.Length);
        stream.Write(lengthBytes, 0, lengthBytes.Length);
        // 再发送完整的加密数据
        stream.Write(encryptedData, 0, encryptedData.Length);
        
        MessageBox.Show("File sent!");
    }
}

修改后的接收函数

private void ReceiveFileButton_Click(object sender, RoutedEventArgs e)
{
    // 先读取数据长度
    byte[] lengthBuffer = new byte[4];
    int bytesRead = stream.Read(lengthBuffer, 0, lengthBuffer.Length);
    if (bytesRead != 4)
    {
        MessageBox.Show("Failed to read data length!");
        return;
    }
    int dataLength = BitConverter.ToInt32(lengthBuffer, 0);

    // 循环读取所有加密数据,直到全部接收完成
    byte[] encryptedData = new byte[dataLength];
    int totalRead = 0;
    while (totalRead < dataLength)
    {
        bytesRead = stream.Read(encryptedData, totalRead, dataLength - totalRead);
        if (bytesRead == 0)
        {
            MessageBox.Show("Connection closed prematurely!");
            return;
        }
        totalRead += bytesRead;
    }

    byte[] decryptedData = DecryptData(encryptedData);

    Microsoft.Win32.SaveFileDialog saveFileDialog = new Microsoft.Win32.SaveFileDialog
    {
        Filter = "Word Documents|*.docx"
    };

    if (saveFileDialog.ShowDialog() == true)
    {
        string filePath = saveFileDialog.FileName;
        File.WriteAllBytes(filePath, decryptedData);
        MessageBox.Show("File received and saved!");
    }
}

这样就能保证接收端拿到完整的加密数据,解密时不会出现填充验证错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 19:43:09