commit 6e3782b281f08d1e3d5784a83f83758bd16c4d0a Author: HiveBeats Date: Sun Oct 20 23:08:53 2024 +0700 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..add57be --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +bin/ +obj/ +/packages/ +riderModule.iml +/_ReSharper.Caches/ \ No newline at end of file diff --git a/.idea/.idea.Encryption/.idea/.gitignore b/.idea/.idea.Encryption/.idea/.gitignore new file mode 100644 index 0000000..cc12bdf --- /dev/null +++ b/.idea/.idea.Encryption/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/modules.xml +/contentModel.xml +/.idea.Encryption.iml +/projectSettingsUpdater.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.idea.Encryption/.idea/encodings.xml b/.idea/.idea.Encryption/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/.idea/.idea.Encryption/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.Encryption/.idea/indexLayout.xml b/.idea/.idea.Encryption/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.Encryption/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.Encryption/.idea/vcs.xml b/.idea/.idea.Encryption/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/.idea.Encryption/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/AESExample/AESExample.csproj b/AESExample/AESExample.csproj new file mode 100644 index 0000000..2ddbfe3 --- /dev/null +++ b/AESExample/AESExample.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + enable + enable + RsaExample + + + diff --git a/AESExample/Program.cs b/AESExample/Program.cs new file mode 100644 index 0000000..2106a1b --- /dev/null +++ b/AESExample/Program.cs @@ -0,0 +1,86 @@ +// See https://aka.ms/new-console-template for more information + +using System; +using System.Security.Cryptography; +using System.Text; + +namespace xxx +{ + public class AesEncryption + { + + + } + internal class Program + { + static void Main(string[] args) + { + string plaintext = "Hello, World!"; + Console.WriteLine(plaintext); + // Generate a random key and IV + byte[] key = new byte[32]; // 256-bit key + byte[] iv = new byte[16]; // 128-bit IV + using (var rng = new RNGCryptoServiceProvider()) + { + rng.GetBytes(key); + rng.GetBytes(iv); //IV == initialization vector + } + + // Encrypt + byte[] ciphertext = Encrypt(plaintext, key, iv); + string encryptedText = Convert.ToBase64String(ciphertext); + Console.WriteLine("Encrypted Text: " + encryptedText); + // Decrypt + byte[] bytes = Convert.FromBase64String(encryptedText); + string decryptedText = Decrypt(bytes, key, iv); + Console.WriteLine("Decrypted Text: " + decryptedText); + //point + Console.ReadLine(); + } + + public static byte[] Encrypt(string plaintext, byte[] key, byte[] iv) + { + using (Aes aesAlg = Aes.Create()) + { + aesAlg.Key = key; + aesAlg.IV = iv; + ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); + byte[] encryptedBytes; + using (var msEncrypt = new System.IO.MemoryStream()) + { + using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) + { + byte[] plainBytes = Encoding.UTF8.GetBytes(plaintext); + csEncrypt.Write(plainBytes, 0, plainBytes.Length); + } + encryptedBytes = msEncrypt.ToArray(); + } + return encryptedBytes; + } + } + + public static string Decrypt(byte[] ciphertext, byte[] key, byte[] iv) + { + using (Aes aesAlg = Aes.Create()) + { + aesAlg.Key = key; + aesAlg.IV = iv; + ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); + byte[] decryptedBytes; + using (var msDecrypt = new System.IO.MemoryStream(ciphertext)) + { + using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) + { + using (var msPlain = new System.IO.MemoryStream()) + { + csDecrypt.CopyTo(msPlain); + decryptedBytes = msPlain.ToArray(); + } + } + } + return Encoding.UTF8.GetString(decryptedBytes); + } + } + } + +} \ No newline at end of file diff --git a/Encryption.sln b/Encryption.sln new file mode 100644 index 0000000..ff4f35a --- /dev/null +++ b/Encryption.sln @@ -0,0 +1,40 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Encryption", "Encryption\Encryption.csproj", "{27A19568-4CA9-4D3A-90CE-5E50B0D5E877}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TcpServer", "TcpServer\TcpServer.csproj", "{FC388EC8-8FF0-473D-8F5D-3C9A9DE9D035}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TcpClient", "TcpClient\TcpClient.csproj", "{3BC68F54-52EE-4A2C-BC7C-AA332BBA3669}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AESExample", "AESExample\AESExample.csproj", "{C200386F-913B-4415-B69C-1BC693E2BCCF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RSAExample", "RSAExample\RSAExample.csproj", "{F97848B3-93A6-48D9-BB3D-A37D81B59AD3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {27A19568-4CA9-4D3A-90CE-5E50B0D5E877}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {27A19568-4CA9-4D3A-90CE-5E50B0D5E877}.Debug|Any CPU.Build.0 = Debug|Any CPU + {27A19568-4CA9-4D3A-90CE-5E50B0D5E877}.Release|Any CPU.ActiveCfg = Release|Any CPU + {27A19568-4CA9-4D3A-90CE-5E50B0D5E877}.Release|Any CPU.Build.0 = Release|Any CPU + {FC388EC8-8FF0-473D-8F5D-3C9A9DE9D035}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FC388EC8-8FF0-473D-8F5D-3C9A9DE9D035}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FC388EC8-8FF0-473D-8F5D-3C9A9DE9D035}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FC388EC8-8FF0-473D-8F5D-3C9A9DE9D035}.Release|Any CPU.Build.0 = Release|Any CPU + {3BC68F54-52EE-4A2C-BC7C-AA332BBA3669}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3BC68F54-52EE-4A2C-BC7C-AA332BBA3669}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3BC68F54-52EE-4A2C-BC7C-AA332BBA3669}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3BC68F54-52EE-4A2C-BC7C-AA332BBA3669}.Release|Any CPU.Build.0 = Release|Any CPU + {C200386F-913B-4415-B69C-1BC693E2BCCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C200386F-913B-4415-B69C-1BC693E2BCCF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C200386F-913B-4415-B69C-1BC693E2BCCF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C200386F-913B-4415-B69C-1BC693E2BCCF}.Release|Any CPU.Build.0 = Release|Any CPU + {F97848B3-93A6-48D9-BB3D-A37D81B59AD3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F97848B3-93A6-48D9-BB3D-A37D81B59AD3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F97848B3-93A6-48D9-BB3D-A37D81B59AD3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F97848B3-93A6-48D9-BB3D-A37D81B59AD3}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/Encryption/Encryption.csproj b/Encryption/Encryption.csproj new file mode 100644 index 0000000..2f4fc77 --- /dev/null +++ b/Encryption/Encryption.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + diff --git a/Encryption/Program.cs b/Encryption/Program.cs new file mode 100644 index 0000000..614aa5c --- /dev/null +++ b/Encryption/Program.cs @@ -0,0 +1,24 @@ +// See https://aka.ms/new-console-template for more information + + +using System.Numerics; +using System.Text; +using Encryption; + +var gen = new RsaKeyGenerator(512); +var keys = gen.GetKeys(); + +Console.WriteLine($"====BEGIN RSA PRIVATE KEY====\n{keys.PrivateKey}\n====END RSA PRIVATE KEY===="); +Console.WriteLine($"====BEGIN RSA PUBLIC KEY====\n{keys.PublicKey}\n====END RSA PUBLIC KEY===="); + + + +var message = "Привет, мир!"; +var encryptedMessage = RSA.Encrypt(keys.PublicKey, Encoding.UTF8.GetBytes(message)); +var decryptedMessage = RSA.Decrypt(keys.PrivateKey, encryptedMessage); + +Console.WriteLine($"Original message: {message}"); +Console.WriteLine($"Cipher: {Convert.ToBase64String(encryptedMessage)}"); +Console.WriteLine($"DecryptedMessage: {Encoding.UTF8.GetString(decryptedMessage)}"); + + diff --git a/Encryption/RSA.cs b/Encryption/RSA.cs new file mode 100644 index 0000000..645e14d --- /dev/null +++ b/Encryption/RSA.cs @@ -0,0 +1,49 @@ +using System.Numerics; +using System.Security.Cryptography; + +namespace Encryption; + +public static class RSA +{ + // Hash funciton to use. Only cryptographic hash functions can be used. + private static Func _hash = (data) => SHA256.HashData(data); + + /// + /// Encrypt message with (message^e) mod n + /// + /// + /// + /// + public static byte[] Encrypt(RsaPublicKey publicKey, byte[] data) + { + var dataAsBigint = new BigInteger(data); + return BigInteger.ModPow(dataAsBigint, publicKey.E, publicKey.N).ToByteArray(); + } + + /// + /// Decrypt cipher with (cipher^d) mod n + /// + /// + /// + /// + public static byte[] Decrypt(RsaPrivateKey privateKey, byte[] data) + { + var dataAsBigint = new BigInteger(data); + return BigInteger.ModPow(dataAsBigint, privateKey.D, privateKey.N).ToByteArray(); + } + + public static byte[] Sign(RsaPrivateKey privateKey, byte[] data) + { + var dataHash = _hash(data); + return Decrypt(privateKey, dataHash); + } + + public static bool Verify(RsaPublicKey publicKey, byte[] data, byte[] signature) + { + var dataHash = _hash(data); + var encryptedSignature = Encrypt(publicKey, signature); + + return dataHash == encryptedSignature; + } + +} \ No newline at end of file diff --git a/Encryption/RsaKeyGenerator.cs b/Encryption/RsaKeyGenerator.cs new file mode 100644 index 0000000..d451cd8 --- /dev/null +++ b/Encryption/RsaKeyGenerator.cs @@ -0,0 +1,297 @@ +using System.Numerics; +using System.Text; +using System.Text.Json; + +namespace Encryption; + +public class RsaKeyGenerator +{ + private static readonly Random _random = new(Environment.TickCount); + private BigInteger _d; + private BigInteger _e; + private BigInteger _n; + private BigInteger _p; + private BigInteger _q; + private BigInteger _r; + + public RsaKeyGenerator(int bitLength) + { + //The "e" value for low compute time RSA encryption. + //Only has two bits of value 1. + const int e = 0x10001; + + //Generating primes, checking if the GCD of (n-1)(p-1) and e is 1. + do + { + _q = FindPrime(bitLength / 2); + } while (_q % e == 1); + + do + { + _p = FindPrime(bitLength / 2); + } while (_p % e == 1); + + //Setting n as QP, phi (represented here as r) to tortiary. + _n = _q * _p; + _r = (_p - 1) * (_q - 1); + + //Computing D such that ed = 1%x. + _d = ModularInverse(e, _r); + + _e = e; + } + + /// + /// Finds a prime of the given bit length, to be used as n and p in RSA key calculations. + /// + /// + /// + private static BigInteger FindPrime(int bitlength) + { + //Generating a random number of bit length. + if (bitlength % 8 != 0) throw new Exception("Invalid bit length for key given, cannot generate primes."); + + //Filling bytes with pseudorandom. + var randomBytes = new byte[bitlength / 8 + 1]; + _random.NextBytes(randomBytes); + //Making the extra byte 0x0 so the BigInts are unsigned (little endian). + randomBytes[randomBytes.Length - 1] = 0x0; + + //Setting the bottom bit and top two bits of the number. + //This ensures the number is odd, and ensures the high bit of N is set when generating keys. + SetBitInByte(0, ref randomBytes[0]); + SetBitInByte(7, ref randomBytes[randomBytes.Length - 2]); + SetBitInByte(6, ref randomBytes[randomBytes.Length - 2]); + + while (true) + { + //Performing a Rabin-Miller primality test. + var isPrime = RabinMillerTest(randomBytes, 40); + if (isPrime) + { + break; + } + + IncrementByteArrayLE(ref randomBytes, 2); + var upper_limit = new byte[randomBytes.Length]; + + //Clearing upper bit for unsigned, creating upper and lower bounds. + upper_limit[randomBytes.Length - 1] = 0x0; + var upper_limit_bi = new BigInteger(upper_limit); + var lower_limit = upper_limit_bi - 20; + var current = new BigInteger(randomBytes); + + if (lower_limit < current && current < upper_limit_bi) + //Failed to find a prime, returning -1. + //Reached limit with no solutions. + return new BigInteger(-1); + } + + //Returning working BigInt. + return new BigInteger(randomBytes); + } + + /// + /// A Rabin Miller primality test which returns true or false. + /// + /// The number to check for being likely prime. + /// + private static bool RabinMillerTest(BigInteger source, int certainty) + { + //Filter out basic primes. + if (source == 2 || source == 3) return true; + //Below 2, and % 0? Not prime. + if (source < 2 || source % 2 == 0) return false; + + //Finding even integer below number. + var d = source - 1; + var s = 0; + + while (d % 2 == 0) + { + d /= 2; + s += 1; + } + + //Getting a random BigInt using bytes. + var rng = new Random(Environment.TickCount); + var bytes = new byte[source.ToByteArray().LongLength]; + BigInteger a; + + //Looping to check random factors. + for (var i = 0; i < certainty; i++) + { + do + { + //Generating new random bytes to check as a factor. + rng.NextBytes(bytes); + a = new BigInteger(bytes); + } while (a < 2 || a >= source - 2); + + //Checking for x=1 or x=s-1. + var x = BigInteger.ModPow(a, d, source); + if (x == 1 || x == source - 1) continue; + + //Iterating to check for prime. + for (var r = 1; r < s; r++) + { + x = BigInteger.ModPow(x, 2, source); + if (x == 1) + return false; + if (x == source - 1) break; + } + + if (x != source - 1) return false; + } + + //All tests have failed to prove composite, so return prime. + return true; + } + + /// + /// An overload wrapper for the RabinMillerTest which accepts a byte array. + /// + /// + /// + /// + private static bool RabinMillerTest(byte[] bytes, int acc_amt) + { + var b = new BigInteger(bytes); + return RabinMillerTest(b, acc_amt); + } + + /// + /// Performs a modular inverse on u and v, + /// such that d = gcd(u,v); + /// + /// D, such that D = gcd(u,v). + private static BigInteger ModularInverse(BigInteger u, BigInteger v) + { + //Declaring new variables on the heap. + BigInteger inverse, u1, u3, v1, v3, t1, t3, q = new(); + //Staying on the stack, quite small, so no need for extra memory time. + BigInteger iteration; + + //Stating initial variables. + u1 = 1; + u3 = u; + v1 = 0; + v3 = v; + + //Beginning iteration. + iteration = 1; + while (v3 != 0) + { + //Divide and sub q, t3 and t1. + q = u3 / v3; + t3 = u3 % v3; + t1 = u1 + q * v1; + + //Swap variables for next pass. + u1 = v1; + v1 = t1; + u3 = v3; + v3 = t3; + iteration = -iteration; + } + + if (u3 != 1) + //No inverse, return 0. + return 0; + if (iteration < 0) + inverse = v - u1; + else + inverse = u1; + + //Return. + return inverse; + } + + + /// + /// Returns the greatest common denominator of both BigIntegers given. + /// + /// The GCD of A and B. + private static BigInteger GCD(BigInteger a, BigInteger b) + { + //Looping until the numbers are zero values. + while (a != 0 && b != 0) + if (a > b) + a %= b; + else + b %= a; + + //Returning check. + return a == 0 ? b : a; + } + + /// + /// Sets a bit in a given ref byte, using an index from 0-7 from the right. + /// + /// The index of the bit number from the lesser side of the byte. + /// The referenced byte to set. + private static void SetBitInByte(int bitNumFromRight, ref byte toSet) + { + var mask = (byte)(1 << bitNumFromRight); + toSet |= mask; + } + + /// + /// Increments the byte array as a whole, by a given amount. Assumes little endian. + /// Assumes unsigned randomBytes. + /// + private static void IncrementByteArrayLE(ref byte[] randomBytes, int amt) + { + var n = new BigInteger(randomBytes); + n += amt; + randomBytes = n.ToByteArray(); + } + + /// + /// Decrements the byte array as a whole, by a given amount. Assumes little endian. + /// Assumes unsigned randomBytes. + /// + private static void DecrementByteArrayLE(ref byte[] randomBytes, int amt) + { + var n = new BigInteger(randomBytes); + n -= amt; + randomBytes = n.ToByteArray(); + } + + public RsaKeyPair GetKeys() + { + return new RsaKeyPair(new RsaPrivateKey(_d, _n), new RsaPublicKey(_e, _n)); + } +} + +public record RsaPublicKey(BigInteger E, BigInteger N) +{ + public override string ToString() + { + return Convert.ToBase64String(Encoding.UTF8.GetBytes($"e:{E};n:{N}")); + } + + public string ToJsonString() + { + var e = E.ToString(); + var n = N.ToString(); + + var obj = new Dictionary + { + ["E"] = e, + ["N"] = n, + }; + + return JsonSerializer.Serialize(obj); + } +} + +public record RsaPrivateKey(BigInteger D, BigInteger N) +{ + public override string ToString() + { + return Convert.ToBase64String(Encoding.UTF8.GetBytes($"d:{D};n:{N}")); + } +} + +public record RsaKeyPair(RsaPrivateKey PrivateKey, RsaPublicKey PublicKey); \ No newline at end of file diff --git a/RSAExample/Program.cs b/RSAExample/Program.cs new file mode 100644 index 0000000..b29b3e7 --- /dev/null +++ b/RSAExample/Program.cs @@ -0,0 +1,25 @@ +using System.Security.Cryptography; +using System.Text; + +using (RSA rsa = RSA.Create()) +{ + rsa.KeySize = 2048; + + // Export the public key + var publicKey = rsa.ExportRSAPublicKey(); + Console.WriteLine("Public Key: " + Convert.ToBase64String(publicKey)); + + // Export the private key + var privateKey = rsa.ExportRSAPrivateKey(); + Console.WriteLine("Private Key: " + Convert.ToBase64String(privateKey)); + + var message = "Some secret message"; + Console.WriteLine("Original message: " + message); + + // There are some of the paddings, but most common is PKCS + var encryptedBytes = rsa.Encrypt(Encoding.UTF8.GetBytes(message), RSAEncryptionPadding.Pkcs1); + Console.WriteLine("Encrypted message: " + Convert.ToBase64String(encryptedBytes)); + + var decryptedBytes = rsa.Decrypt(encryptedBytes, RSAEncryptionPadding.Pkcs1); + Console.WriteLine("Decrypted message: " + Encoding.UTF8.GetString(decryptedBytes)); +} \ No newline at end of file diff --git a/RSAExample/RSAExample.csproj b/RSAExample/RSAExample.csproj new file mode 100644 index 0000000..2f4fc77 --- /dev/null +++ b/RSAExample/RSAExample.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + diff --git a/TcpClient/Program.cs b/TcpClient/Program.cs new file mode 100644 index 0000000..188bb05 --- /dev/null +++ b/TcpClient/Program.cs @@ -0,0 +1,142 @@ +using System; +using System.Diagnostics; +using System.Net.Sockets; +using System.Numerics; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using Encryption; + +enum ConversationState +{ + Initial = 0, + RsaPublicKeyGiven, + AesKeySended, + RsaPublicKeySended, + AesKeyGiven +} + +class TcpClientProgram +{ + static void Main(string[] args) + { + TcpClient client = new TcpClient(); + client.Connect("127.0.0.1", 5000); + Console.WriteLine("Connected to server."); + + NetworkStream stream = client.GetStream(); + bool isRunning = true; + ConversationState state = ConversationState.Initial; + while (isRunning) + { + byte[] buffer = new byte[4096]; + int bytesRead; + + RsaPublicKey publicKey = null; + RsaPrivateKey privateKey = null; + string? aesKey = null; + + while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0) + { + string receivedData = Encoding.ASCII.GetString(buffer, 0, bytesRead); + Console.WriteLine("Received: " + receivedData); + + // Split the message into command and data + string[] messageParts = receivedData.Split(new char[] { ':' }, 2); + if (messageParts.Length < 2) continue; // Ignore malformed messages + + string command = messageParts[0]; + string message = messageParts[1]; + + switch (command) + { + case "PUBKEY": + Console.WriteLine("Received RSA Public Key: " + message); + state = ConversationState.RsaPublicKeyGiven; + var json = Convert.FromBase64String(message); + var dict = JsonSerializer.Deserialize>(json); + publicKey = new RsaPublicKey(BigInteger.Parse((string)dict["E"]), + BigInteger.Parse((string)dict["N"])); + break; + case "AESKEY": + Console.WriteLine("Received Encrypted AES Key: " + message); + state = ConversationState.AesKeyGiven; + aesKey = Encoding.UTF8.GetString(Convert.FromBase64String(message)); + break; + case "MSG": + Console.WriteLine("Received AES Encrypted Message: " + message); + // todo: decrypt message and print it + var decodedMessage = Convert.FromBase64String(message); + var decryptedMessageBytes = RSA.Decrypt(privateKey, decodedMessage); + Console.WriteLine($"Decrypted message: {Encoding.UTF8.GetString(decryptedMessageBytes)}"); + break; + case "EXIT": + Console.WriteLine("Client is disconnecting..."); + client.Close(); + return; + default: + Console.WriteLine("Unknown command received: " + command); + break; + } + } + + Console.WriteLine("1. Send RSA public key"); + Console.WriteLine("4. Send AES key encrypted with RSA"); + Console.WriteLine("5. Send/receive AES encrypted message"); + Console.WriteLine("6. Exit"); + + string choice = Console.ReadLine(); + if (state == ConversationState.RsaPublicKeyGiven && choice == "1") + { + Console.WriteLine("You already recieved Public Key from participant. It's time to send him your AES key back"); + continue; + } + + if (state == ConversationState.AesKeyGiven && choice == "4") + { + Console.WriteLine("You already have AES key from participant. It's time to send him encrypted message."); + } + + switch (choice) + { + case "1": + // Placeholder: Send a simulated RSA public key + var json = publicKey.ToJsonString(); + var message = Convert.ToBase64String(Encoding.UTF8.GetBytes(json)); + SendMessage(stream, "PUBKEY", message); + state = ConversationState.RsaPublicKeySended; + break; + case "4": + // Placeholder: Send an AES key encrypted with RSA + aesKey = ""; //todo: generate random + var aesKeyEncryptedBytes = RSA.Encrypt(publicKey, Encoding.UTF8.GetBytes(aesKey)); + SendMessage(stream, "AESKEY", Convert.ToBase64String(aesKeyEncryptedBytes)); + state = ConversationState.AesKeySended; + break; + case "5": + string msg = Console.ReadLine(); + // Placeholder: Send an AES encrypted message + SendMessage(stream, "MSG", Convert.ToBase64String(RSA.Encrypt(publicKey, Encoding.UTF8.GetBytes(msg)))); + break; + case "6": + SendMessage(stream, "EXIT", "Disconnecting"); + isRunning = false; + Console.WriteLine("Exiting..."); + break; + default: + Console.WriteLine("Invalid choice. Please try again."); + break; + } + } + + client.Close(); + } + + static void SendMessage(NetworkStream stream, string command, string message) + { + string formattedMessage = $"{command}:{message}"; + byte[] data = Encoding.ASCII.GetBytes(formattedMessage); + stream.Write(data, 0, data.Length); + Console.WriteLine($"Sent [{command}]: {message}"); + } +} diff --git a/TcpClient/TcpClient.csproj b/TcpClient/TcpClient.csproj new file mode 100644 index 0000000..6638425 --- /dev/null +++ b/TcpClient/TcpClient.csproj @@ -0,0 +1,14 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + diff --git a/TcpServer/Program.cs b/TcpServer/Program.cs new file mode 100644 index 0000000..e59feea --- /dev/null +++ b/TcpServer/Program.cs @@ -0,0 +1,129 @@ +// See https://aka.ms/new-console-template for more information + +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; + +class TcpServer +{ + private static TcpListener server; + private static bool isRunning = true; + private static List connectedClients = new List(); + + static void Main(string[] args) + { + server = new TcpListener(IPAddress.Any, 5000); + server.Start(); + Console.WriteLine("Server started on port 5000."); + + while (isRunning) + { + var client = server.AcceptTcpClient(); + Console.WriteLine("Client connected!"); + + lock (connectedClients) + { + connectedClients.Add(client); // Add new client to the list + } + + // Handle each client connection in a new thread + Thread clientThread = new Thread(HandleClient); + clientThread.Start(client); + } + } + + private static void HandleClient(object obj) + { + TcpClient client = (TcpClient)obj; + NetworkStream stream = client.GetStream(); + byte[] buffer = new byte[4096]; + int bytesRead; + + try + { + while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0) + { + string receivedData = Encoding.ASCII.GetString(buffer, 0, bytesRead); + Console.WriteLine("Received: " + receivedData); + + // Split the message into command and data + string[] messageParts = receivedData.Split(new char[] { ':' }, 2); + if (messageParts.Length < 2) continue; // Ignore malformed messages + + string command = messageParts[0]; + string message = messageParts[1]; + + switch (command) + { + case "PUBKEY": + Console.WriteLine("Received RSA Public Key: " + message); + // Broadcast the message to all connected clients (excluding the sender) + BroadcastMessageToClients(client, $"{command}:{message}"); + break; + case "AESKEY": + Console.WriteLine("Received Encrypted AES Key: " + message); + // Broadcast the message to all connected clients (excluding the sender) + BroadcastMessageToClients(client, $"{command}:{message}"); + break; + case "MSG": + Console.WriteLine("Received AES Encrypted Message: " + message); + // Broadcast the message to all connected clients (excluding the sender) + BroadcastMessageToClients(client, $"{command}:{message}"); + break; + case "EXIT": + Console.WriteLine("Client is disconnecting..."); + client.Close(); + lock (connectedClients) + { + connectedClients.Remove(client); // Remove client from the list + } + return; + default: + Console.WriteLine("Unknown command received: " + command); + break; + } + } + } + catch (Exception ex) + { + Console.WriteLine("Client disconnected with error: " + ex.Message); + } + finally + { + client.Close(); + lock (connectedClients) + { + connectedClients.Remove(client); + } + Console.WriteLine("Client disconnected."); + } + } + + private static void BroadcastMessageToClients(TcpClient senderClient, string message) + { + byte[] data = Encoding.ASCII.GetBytes("Broadcast: " + message); + + lock (connectedClients) + { + foreach (var client in connectedClients) + { + // Send to all clients except the sender + if (client != senderClient) + { + try + { + NetworkStream stream = client.GetStream(); + stream.Write(data, 0, data.Length); + } + catch (Exception ex) + { + Console.WriteLine("Error sending to client: " + ex.Message); + } + } + } + } + } +} diff --git a/TcpServer/TcpServer.csproj b/TcpServer/TcpServer.csproj new file mode 100644 index 0000000..2f4fc77 --- /dev/null +++ b/TcpServer/TcpServer.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + +