当传入的消息使用AES加密时,我需要从NetworkStream读取多少字节



我是一名学生,不习惯c#或"网络编程"。我们需要制作一个客户端-服务器程序,支持文件传输。我正试图将有关文件的一些信息发送到服务器,但若服务器读取的字节数比客户端发送的字节数多或少,程序就会出现异常。我在问服务器端怎么能提前知道,传入加密消息的字节大小。

例如:如果加密发送的消息大小为48字节,而在服务器端,我从流中读取1024字节(在本例中为48字节(,则程序chrash。。。问题是,服务器事先不知道加密消息的大小。。。

我的程序甚至还没有接近最终产品,我还有很多其他问题(在读/写NetworkStream之前,GUI在没有MessageBox.Show("(消息的情况下冻结(,但这是我目前的问题。我也不知道如何在AES加密中在服务器和客户端设置相同的IV,而不声明全局IV。

这是整个代码:

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.IO;
using System.Security.Cryptography;
using System.Linq;
namespace Kodiranje
{
public partial class Form1 : Form
{
const int port = 22222;
const string ip = "127.0.0.1";
IPAddress ipServer = IPAddress.Parse(ip);
TcpListener server = null;
TcpClient client = null;
Thread thServer = null;
NetworkStream dataStream = null;
string receivedMessage = "";
bool? izbira = null;
Aes myAes = Aes.Create();
bool serverRunning = true;
bool datotekaIzbrana = false;
byte[] iv = null;
string filePath, fileName, fileExtension;
OpenFileDialog openFileDialog;
long fileSize;
long kolicnik;
long ostanek;

public Form1()
{
InitializeComponent();
iv = myAes.IV;
thServer = new Thread(new ThreadStart(startServer));
thServer.IsBackground = true;
thServer.Start();
myAes.Padding = PaddingMode.PKCS7;
iv = myAes.IV;  // global IV set - how to avoid that?
}
void startServer()
{
server = new TcpListener(ipServer, port);
server.Start();
textBox4.Invoke(new Action(() => textBox4.AppendText("Strežnik: zagnan na: IP: " + ip + ", port:" + port)));
client = new TcpClient();
client = server.AcceptTcpClient();
NetworkStream dataStream = client.GetStream();
textBox4.Invoke(new Action(() => textBox4.AppendText(Environment.NewLine + "Strežnik: Sprejet nov uporabnik")));

if (izbira == true)
{
byte[] serverPublicKey;
ECDiffieHellmanCng serverDH = new ECDiffieHellmanCng();
serverDH.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
serverDH.HashAlgorithm = CngAlgorithm.Sha256;
serverPublicKey = serverDH.PublicKey.ToByteArray();

byte[] message = new byte[1024];
while (!dataStream.DataAvailable) { }
if (dataStream.DataAvailable)
try
{
dataStream.Read(message, 0, message.Length);
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
byte[] serverCommoneKey = serverDH.DeriveKeyMaterial(CngKey.Import(message, CngKeyBlobFormat.EccPublicBlob));
textBox4.Invoke(new Action(() => textBox4.AppendText(Environment.NewLine + "Strežnik: Dobil sem sporočilo: " + receivedMessage)));

dataStream.Write(serverPublicKey, 0, serverPublicKey.Length);

// Here I'm reading sent message and I decrypt it, but if the size of new byte[] is 
not the same as the size of recieved message my program chrashes... how can i avoid               
chrasing wihtout knowing the message size, and if I for instance set reading size to 
1024 bytes?                   
byte[] encryptedMessage = new byte[48]; // for this file i know the sent size from 
client but in reality, I wouldn't know the exact size...
while (!dataStream.DataAvailable) { }
MessageBox.Show(""); // GUI freezes without this message
dataStream.Read(encryptedMessage, 0, encryptedMessage.Length);
string prejetoSporocilo = odkodirajString(serverCommoneKey, encryptedMessage);
MessageBox.Show("Odkodirano prejeto sporočilo:" + prejetoSporocilo);
}

else // irrelevant
{
byte[] message = new byte[1024];
message = Encoding.UTF8.GetBytes("serbus");
dataStream.Write(message, 0, message.Length);

}
while (serverRunning) { }
}

void button1_Click(object sender, EventArgs e)
{
if (izbira == null)
{
this.textBox4.AppendText(Environment.NewLine + "Izbrati morate ali boste datoteko prenesli ali poslali!");
return;
}
if (!datotekaIzbrana)
{
MessageBox.Show("Izberite datoteko!");
return;
}
this.button2.Enabled = true;
this.button4.Enabled = false;
this.button1.Enabled = false;
this.button3.Enabled = true;
client = new TcpClient();
IPAddress insertedIp = IPAddress.Parse(textBox1.Text);

byte[] clientPublicKey;
ECDiffieHellmanCng clientDH = new ECDiffieHellmanCng();
clientDH.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
clientDH.HashAlgorithm = CngAlgorithm.Sha256;
clientPublicKey = clientDH.PublicKey.ToByteArray();

client.Connect(insertedIp, Convert.ToInt32(textBox3.Text));
dataStream = client.GetStream();

if (izbira == true)
{
dataStream.Write(clientPublicKey, 0, clientPublicKey.Length);
byte[] message = new byte[1024];
MessageBox.Show(""); // GUI freezes without this message
while (true)
{
if (dataStream.DataAvailable)
{
dataStream.Read(message, 0, message.Length);
this.textBox4.AppendText(Environment.NewLine + "Client: Dobil sem sporočilo: " + receivedMessage);
break;
}
}
byte[] clientCommoneKey = clientDH.DeriveKeyMaterial(CngKey.Import(message, CngKeyBlobFormat.EccPublicBlob));

message = new byte[1024];
/// here I'm trying to send file information, then encrypt it and send it to server
kolicnik = fileSize / 1024;
ostanek = fileSize % 1024;
string glava = fileName + "|" + fileExtension + "|" + fileSize.ToString() + "|" + kolicnik.ToString() + "|" + ostanek + "|";
//MessageBox.Show(glava);
message = Encoding.UTF8.GetBytes(glava);
MessageBox.Show("velikost stringa: " + glava.Length + " velikost bitov: " + message.Length);
message = kodirajString(clientCommoneKey,glava);
MessageBox.Show("Zakodiran size: " + message.Length);
dataStream.Write(message, 0, message.Length);


///

}

else // irrelevant
{
byte[] message = new byte[1024];
while (!dataStream.DataAvailable)
{ }
dataStream.Read(message, 0, message.Length);
receivedMessage = Encoding.UTF8.GetString(message);
this.textBox4.AppendText(Environment.NewLine + "Strežnik: Dobil sem sporočilo: " + receivedMessage);
}
}

void buttonUpload_Click(object sender, EventArgs e)
{
izbira = true;
this.buttonDownload.Enabled = false;
this.button4.Enabled = true;
}
void buttonDownload_Click(object sender, EventArgs e) // irrelevant
{
izbira = false;
this.buttonUpload.Enabled = false;
this.button2.Text = "Prenesi";
}
void button3_Click(object sender, EventArgs e)
{
serverRunning = false;
}
void button4_Click(object sender, EventArgs e) // choose file
{
openFileDialog = new OpenFileDialog();
openFileDialog.Title = "IZBERI DATOTEKO";
openFileDialog.Filter = "GIF Image|*.gif|text file|*.txt|PDF file|*.pdf";
if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
//break;
datotekaIzbrana = true;
filePath = openFileDialog.FileName;
this.textBox4.AppendText(Environment.NewLine + "Pot do datoteke:" + filePath);
fileName = openFileDialog.SafeFileName;
fileExtension = Path.GetExtension(filePath);
string getPath = @filePath;
FileInfo info = new FileInfo(getPath);
Stream file = File.OpenRead(filePath);
fileSize = info.Length;
fileName = Path.GetFileNameWithoutExtension(info.Name);

}
else {
MessageBox.Show("IZBERITE DATOTEKO!");
}

}
void button2_Click(object sender, EventArgs e)
{
}
byte[] kodirajString(byte[] commonKey, string messageToEncrypt)  // encrypt message
{
myAes = Aes.Create();
myAes.Padding = PaddingMode.PKCS7;
myAes.Key = commonKey;
myAes.IV = iv;
ICryptoTransform encryptor = myAes.CreateEncryptor(myAes.Key, iv);
byte[] encrypted;
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(messageToEncrypt);
}
encrypted = msEncrypt.ToArray();
int s = encrypted.Length;
MessageBox.Show("Velikost " + s.ToString());
return encrypted;
}
}
}

string odkodirajString(byte[] commonKey, byte[] encryptedMessage) {  // decrypt message
myAes = Aes.Create();
myAes.Padding = PaddingMode.PKCS7;
myAes.Key = commonKey;
// Create a decryptor to perform the stream transform.
ICryptoTransform decryptor = myAes.CreateDecryptor(myAes.Key, iv);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(encryptedMessage))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
string plaintext;
plaintext = srDecrypt.ReadToEnd();
MessageBox.Show("Sprejeto sporočilo " + plaintext);
return plaintext;
}
}
}
}
}
}

我提前感谢你!

您需要在TCP之上构建一个协议。

例如

[char[6]] VersionCode
[int] length
[byte[length]] payload

现在您可以读取前10个字节。检查版本代码是否与您的程序版本匹配,然后您可以从中获得[length],它将告诉您要读取多少字节。

最新更新