我是一名学生,不习惯使用C#或“网络编程”。我们需要制作一个支持文件传输的客户端服务器程序。我正在尝试将有关文件的某些信息发送到服务器,但是如果服务器读取的字节多于或少于客户机发送的字节,则程序会给出异常。我问服务器端如何事先知道传入的加密消息的字节大小。
例如:如果加密的已发送消息的大小为48个字节,并且在服务器端,我从流中读取了1024个字节(而不是完全相同的大小-在此示例中为48个),则程序崩溃...问题是,服务器事先不知道加密邮件的大小...
我的程序甚至还没有接近最终产品,我还有很多其他问题(GUI冻结,在读取/写入NetworkStream之前没有MessageBox.Show(“”)消息),但这是我当前的问题。我也不知道如何在不声明全局IV的情况下在AES加密中在服务器端和客户端设置相同的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],该长度将告诉您要读取的字节数。