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
相关产品推荐
相关产品推荐

