简体   繁体   中英

How many bytes do I need to read from NetworkStream, when message incoming is encrypted with AES?

I am a student and I am not used to c# or "network programing". We need to make a client server program, that supports file transfering. I'm trying to send some information about the file to server, but if server reads more or less bytes, than client sent, program gives exception. I am asking how can the server side in advance know, the byte size of incoming encrypted message.

For example: if encrypted sent messages size is 48 bytes and on the server side I read 1024 (and not the exact same size - in this example 48) bytes from stream, program chrashes... The problem is, that the server doesn't know encrypted message size in advance...

My program is not even near the final product and I have many other issues too(with GUI freezing without MessageBox.Show("") messages before reading/writing to NetworkStream), but this is my current question. I also don't know how to set the same IV on server and client side in AES ecncryption, without declaring global IV.

Here is the whole code:

    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;
                    }
                }
            }
        }

    }
}

I thank you in advance!

You need to build a protocol over the top of TCP.

eg,

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

Now you can read the first 10 bytes. Check that the Version code matches your programs version, and then from that you can get the [length] which will tell you how many bytes to read.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM