3

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;
    }
}
Bruno
  • 119,590
  • 31
  • 270
  • 376
Javanese Girl
  • 337
  • 2
  • 6
  • 14

4 Answers4

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

Java Devil
  • 10,629
  • 7
  • 33
  • 48
user1689716
  • 389
  • 5
  • 12
2

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 SSLSockets tend to do a fair job at behaving similarly to normal Sockets, 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.

Community
  • 1
  • 1
Bruno
  • 119,590
  • 31
  • 270
  • 376
  • Thanks bruno, C# doesn't support SSLEngine.I tried using SSLStream by combining asynchonous but there are still bugs. – Javanese Girl Jan 21 '12 at 06:16
  • @JavaneseGirl, indeed, sorry if I wasn't clear. I was just saying it was generally possible, but you would need something similar to `SSLEngine` (I'm not sure what, you might need an 3rd party library). Although `SSLEngine` (in Java) does most of the actual SSL/TLS work for you, know when to `wrap`/`unwrap` (see its doc) still requires quite a bit of manual work, especially in async mode. To be honest, I don't know whether `SSLStream` supports something similar, but I can't imagine it to be easy to program (in particular due to the theoretical problems I've mentioned). – Bruno Jan 21 '12 at 12:51
  • @JavaneseGirl Thanks for accepting my answer. It seems that you're a relatively new user and it's true SO encourages askers to accept an answer, but don't feel under pressure to do so so quickly. My answer was incomplete (lacking a C# solution) and you might get better answers by leaving it open for longer. – Bruno Jan 21 '12 at 17:47
0

In .NET, use the SslStream Class. It has everything you need to build SSL client/servers in .NET. Very easy to integrate. Works with all versions of .NET.

0

Direct socket communication works on the Session OSI model layer. TLS and SSL work at the Presentation layer (which is higher than session). So no, AFAIK you cannot use sockets directly and have SSL or TLS level security.

Check out the OSI Model

Adam Spicer
  • 2,703
  • 25
  • 37
  • 3
    One of the targets of the design of SSL/TLS was to emulate a "session layer" on top of an existing one (and make the one of top secure). This being said, it's not quite right to jump straight into the OSI model when talking about TCP sockets anyway. The TCP/IP model isn't quite as rigid (or layered); there's a section on this on the wikipedia page you've linked to. – Bruno Jan 20 '12 at 12:48
  • Thanks Bruno, I'll check it out. – Adam Spicer Jan 20 '12 at 12:57