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

问题描述 投票:0回答:1

我是一名学生,不习惯使用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;
                    }
                }
            }
        }

    }
}

我先谢谢你!

c# encryption aes networkstream diffie-hellman
1个回答
0
投票

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

例如

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

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

© www.soinside.com 2019 - 2024. All rights reserved.