Asynchronous Socket With TLS/SSL
Solution 1
Not sure how to do it in C#, but it can be done in Java using the SSLEngine
. This API is generally acknowledged as rather difficult to program. So, yes, it's possible to use asynchronous sockets for SSL/TLS, but I'm not sure what the equivalent of Java's SSLEngine
would be. Perhaps there is another (better?) API for this in C#.
There are a few problems that you will almost inevitably encounter along this path (based on Java experience, but this would apply similarly in C#):
While SSLSocket
s tend to do a fair job at behaving similarly to normal Socket
s, there are slight edge cases due to the nature of SSL/TLS. The effect of these differences is even more important with asynchronous I/O. Some of these problems are described in this (rather long) answer to "Properly closing SSLSocket" (in Java).
In addition, some SSL/TLS behaviours are already ill-defined with respect to the application layer, and get a bit messier with asynchronous behaviour. I have client-certificate renegotiation (or renegotiation in general) in mind. Using SSL/TLS, either party can in principle initiate a renegotiation handshake. This is done for example if you protect only one directory with client-certificates in Apache Httpd, or if only a portion of a web-app requires CLIENT-CERT
in a Java container. When using client-certificate authentication, even IIS uses re-negotation by default. This consists of doing a second handshake during the SSL/TLS connection (effectively to get more information from the client here: its client-certificate).
When it works (usually with blocking I/O), the traffic looks like this (here both SSL
and HTTP
layers):
C->S SSL Client Hello
S->C SSL Server Hello, Certificate, Server Hello Done
C->S SSL Client Key Exchange, Change Cipher Spec, Finished
S->C SSL Change Cipher Spec
(then encrypted)
C->S SSL Finished
C->S HTTP GET /.../
S->C SSL Hello Request
C->S SSL Client Hello
S->C SSL Server Hello, Certificate, Certificate Request, Server Hello Done
C->S SSL Certificate, Client Key Exchange, Certificate Verify, Change Cipher
Spec, Finished
S->C SSL Change Cipher Spec
C->S SSL Finished
S->C HTTP 200 OK
Re-negotiating in asynchronous mode is quite tricky, since the re-negotiation should apply to both sides of the traffic at the same time. Therefore, the underlying properties of the SSL/TLS session may change during its use by the application layer (which isn't normally expected to handle this). One side could still be sending data assuming certain SSL/TLS settings, while the re-negotiation occurs, thereby affecting both sides.
The implementation of all this can be difficult, as shown in this Grizzly issue, for example.
Solution 2
Well I was looking for an answer myself to support TLS / SSL over tcp/ip sockets using C# and ran across this using Stream Sockets
Javanese Girl
Updated on June 04, 2022Comments
-
Javanese Girl almost 2 years
I saw examples programs on MSDN: Asynchronous Socket as below. I've tried the program and run normally. Is it possible Asynchronous Socket to be modified in order to support TLS/SSL? How to do it?
using System; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; // State object for reading client data asynchronously public class StateObject { // Client socket. public Socket workSocket = null; // Size of receive buffer. public const int BufferSize = 1024; // Receive buffer. public byte[] buffer = new byte[BufferSize]; // Received data string. public StringBuilder sb = new StringBuilder(); } public class AsynchronousSocketListener { // Thread signal. public static ManualResetEvent allDone = new ManualResetEvent(false); public static void StartListening() { // Data buffer for incoming data. byte[] bytes = new Byte[1024]; // Establish the local endpoint for the socket. // The DNS name of the computer // running the listener is "host.contoso.com". IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName()); IPAddress ipAddress = ipHostInfo.AddressList[0]; IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000); // Create a TCP/IP socket. Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp ); // Bind the socket to the local endpoint and listen for incoming connections. try { listener.Bind(localEndPoint); listener.Listen(100); while (true) { // Set the event to nonsignaled state. allDone.Reset(); // Start an asynchronous socket to listen for connections. Console.WriteLine("Waiting for a connection..."); listener.BeginAccept(new AsyncCallback(AcceptCallback), listener ); // Wait until a connection is made before continuing. allDone.WaitOne(); } } catch (Exception e) { Console.WriteLine(e.ToString()); } Console.WriteLine("\nPress ENTER to continue..."); Console.Read(); } public static void AcceptCallback(IAsyncResult ar) { // Signal the main thread to continue. allDone.Set(); // Get the socket that handles the client request. Socket listener = (Socket) ar.AsyncState; Socket handler = listener.EndAccept(ar); // Create the state object. StateObject state = new StateObject(); state.workSocket = handler; handler.BeginReceive( state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state); } public static void ReadCallback(IAsyncResult ar) { String content = String.Empty; // Retrieve the state object and the handler socket // from the asynchronous state object. StateObject state = (StateObject) ar.AsyncState; Socket handler = state.workSocket; // Read data from the client socket. int bytesRead = handler.EndReceive(ar); if (bytesRead > 0) { // There might be more data, so store the data received so far. state.sb.Append(Encoding.ASCII.GetString( state.buffer,0,bytesRead)); // Check for end-of-file tag. If it is not there, read // more data. content = state.sb.ToString(); if (content.IndexOf("<EOF>") > -1) { // All the data has been read from the // client. Display it on the console. Console.WriteLine("Read {0} bytes from socket. \n Data : {1}", content.Length, content ); // Echo the data back to the client. Send(handler, content); } else { // Not all data received. Get more. handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state); } } } private static void Send(Socket handler, String data) { // Convert the string data to byte data using ASCII encoding. byte[] byteData = Encoding.ASCII.GetBytes(data); // Begin sending the data to the remote device. handler.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), handler); } private static void SendCallback(IAsyncResult ar) { try { // Retrieve the socket from the state object. Socket handler = (Socket) ar.AsyncState; // Complete sending the data to the remote device. int bytesSent = handler.EndSend(ar); Console.WriteLine("Sent {0} bytes to client.", bytesSent); handler.Shutdown(SocketShutdown.Both); handler.Close(); } catch (Exception e) { Console.WriteLine(e.ToString()); } } public static int Main(String[] args) { StartListening(); return 0; } }