// This file is part of the EonaCat project(s) which is released under the Apache License. // See the LICENSE file or go to https://EonaCat.com/License for full license details. using System.Net.Security; using System.Reflection; using System.Security.Authentication; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using EonaCat.WebSockets; internal class Program { private static AsyncWebSocketClient _client; private static AsyncWebSocketServer _server; private static async Task Main(string[] args) { // Menu loop while (true) { Console.WriteLine("0. Create a HTTPS certificate"); Console.WriteLine("1. Start the server and client"); Console.WriteLine("2. Send Message"); Console.WriteLine("3. Quit"); var choice = Console.ReadLine(); switch (choice) { case "0": CreateCertificate(); break; case "1": await CreateServerAndClientAsync().ConfigureAwait(false); break; case "2": if (_client != null) { Console.Write("Enter message: "); var message = Console.ReadLine(); await _client.SendTextAsync(message).ConfigureAwait(false); } break; case "3": _client?.CloseAsync().ConfigureAwait(false); return; default: Console.WriteLine("Invalid choice. Try again."); break; } } } private static async Task CreateServerAndClientAsync() { var serverUri = "wss://localhost:8443"; var clientUri = "wss://localhost:8443/Welcome"; var certificatePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "localhost.pfx"); var certificatePassword = ""; var requiredPassword = ""; // Start the server StartServer(serverUri, certificatePath, certificatePassword, requiredPassword); // Start the client in the main thread await StartClientAsync(clientUri, certificatePath, certificatePassword).ConfigureAwait(false); Console.WriteLine("Connected to the server."); } private static async Task StartClientAsync(string clientUri, string certificatePath, string certificatePassword) { var configuration = new AsyncWebSocketClientConfiguration(); configuration.SslTargetHost = "localhost"; configuration.SslEncryptionPolicy = EncryptionPolicy.RequireEncryption; configuration.SslCheckCertificateRevocation = false; configuration.SslPolicyErrorsBypassed = true; configuration.SslEnabledProtocols = SslProtocols.Tls12 | SslProtocols.Tls13; configuration.SslClientCertificates = new X509CertificateCollection { new X509Certificate2(certificatePath, certificatePassword) }; _client = new AsyncWebSocketClient(new Uri(clientUri), OnServerTextReceived, OnServerBinaryReceived, OnServerConnected, OnServerDisconnected, OnServerFragmentationStreamOpened, OnServerFragmentationStreamOpened, OnServerFragmentationStreamClosed, configuration); await _client.ConnectAsync().ConfigureAwait(false); } private static Task OnServerFragmentationStreamClosed(AsyncWebSocketClient arg1, byte[] arg2, int arg3, int arg4) { // Do nothing return Task.CompletedTask; } private static Task OnServerFragmentationStreamOpened(AsyncWebSocketClient arg1, byte[] arg2, int arg3, int arg4) { // Do nothing return Task.CompletedTask; } private static Task OnServerDisconnected(AsyncWebSocketClient arg) { Console.WriteLine("Disconnected from server."); return Task.CompletedTask; } private static Task OnServerConnected(AsyncWebSocketClient arg) { Console.WriteLine("Connected to server."); return Task.CompletedTask; } private static Task OnServerBinaryReceived(AsyncWebSocketClient arg1, byte[] bytes, int arg3, int arg4) { Console.WriteLine($"Received binary {bytes} {arg3} {arg4}"); return Task.CompletedTask; } private static Task OnServerTextReceived(AsyncWebSocketClient arg1, string data) { Console.WriteLine($"Received message from server: {data}"); return Task.CompletedTask; } private static void CreateCertificate() { Console.Write("Enter hostname: (default: localhost) "); var hostname = Console.ReadLine(); if (string.IsNullOrWhiteSpace(hostname)) { hostname = "localhost"; } var days = 30; Console.Write("Enter days until expiration: (default: 30 days) "); if (int.TryParse(Console.ReadLine(), out var givenDays)) { days = givenDays; } Console.Write("Enter password, enter to skip: "); var password = Console.ReadLine(); var rsa = RSA.Create(); // Create a certificate request with the specified subject and key pair var request = new CertificateRequest( $"CN={hostname}", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); // Create a self-signed certificate from the certificate request var certificate = request.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddDays(days)); // Export the certificate to a file with password var certBytes = string.IsNullOrEmpty(password) ? certificate.Export(X509ContentType.Pfx) : certificate.Export(X509ContentType.Pfx, password); File.WriteAllBytes($"{hostname}.pfx", certBytes); Console.WriteLine( $"Certificate for {hostname} created successfully and will expire on {certificate.NotAfter}."); Console.WriteLine($"Path: {Path.Combine(AppContext.BaseDirectory, hostname)}.pfx"); } private static void StartServer(string serverUri, string certificatePath, string certificatePassword, string requiredPassword) { // split the server uri to get the port var uri = new Uri(serverUri); var port = uri.Port; var test = new AsyncWebSocketServerModuleCatalog(); test.RegisterModule(new TestModule()); var configuration = new AsyncWebSocketServerConfiguration(); configuration.SslEnabled = true; configuration.SslEncryptionPolicy = EncryptionPolicy.RequireEncryption; configuration.SslCheckCertificateRevocation = false; configuration.SslPolicyErrorsBypassed = true; configuration.SslClientCertificateRequired = true; configuration.SslEnabledProtocols = SslProtocols.Tls12 | SslProtocols.Tls13; configuration.SslServerCertificate = new X509Certificate2(certificatePath, certificatePassword); _server = new AsyncWebSocketServer(port, test, configuration); _server.Listen(); } }