Venelin
Venelin

Reputation: 3308

Convert TCP StreamReader/StreamWriter to SSLStream

Here is my code:

public class ServerClient
{
    public  TcpClient tcp;
    public StreamReader streamReader;
    public StreamWriter streamWriter;
    public int accountId;
    public int connectionId;
    public ServerClient(TcpClient clientSocket)
    {
        tcp = clientSocket;
        streamReader = new StreamReader(tcp.GetStream(), false);
        streamWriter = new StreamWriter(tcp.GetStream());
        clientSocket.NoDelay = true;
    }
}

Here is how i am listening for data, this is called in a while loop:

// Check for message from Client.
NetworkStream s = c.tcp.GetStream();
if (s.DataAvailable)
{
    string data = c.streamReader.ReadLine();

    if (data != null)
    {
        if (ValidateJSON(data))
        {
            OnIncomingData(c, data);
        }                            
    }

}
//continue;

Here is how i check if the client is still connected:

private bool IsConnected(TcpClient c)
{
    try
    {
        if (c != null && c.Client != null && c.Client.Connected)
        {
            if (c.Client.Poll(0, SelectMode.SelectRead))
            {
                return !(c.Client.Receive(new byte[1], SocketFlags.Peek) == 0);
            }

            return true;
        }
        else
        {
            return false;
        }
    }
    catch
    {
        return false;
    }
}

Here is how i send data into the stream:

string JSonData = JsonConvert.SerializeObject(SendData);

c.streamWriter.WriteLine(JSonData);
c.streamWriter.Flush();

Here is my client how it listens for message:

public void ConnectToWorldServer()
{
    if (socketReady)
    {
        return;
    }
    //Default host and port values;
    string host = "127.0.0.1";
    int port = 8080;

    //ClientLoginServer ClientLoginServer = new ClientLoginServer();


    try
    {

        socket = new TcpClient(host, port);
        stream = socket.GetStream();
        socket.NoDelay = true;
        writer = new StreamWriter(stream);
        reader = new StreamReader(stream);
        socketReady = true;
        //Preserve the connection to worldserver thrue scenes
        UnityThread.executeInUpdate(() =>
        {
            DontDestroyOnLoad(worldserverConnection);
        });

        // Start listening for connections.
        while (true)
        {
            keepReading = true;

            while (keepReading)
            {
                keepReading = true;
                if (socketReady)
                {
                    if (stream.DataAvailable)
                    {
                        string data = reader.ReadLine();
                        if (data != null)
                        {
                            UnityThread.executeInUpdate(() =>
                            {
                                OnIncomingData(data);
                            });
                        }
                    }
                }
                System.Threading.Thread.Sleep(1);
            }
            System.Threading.Thread.Sleep(1);
        }
    }
    catch (Exception e)
    {
        Debug.Log("Socket error : " + e.Message);
    }

}

I have red about SSL Stream. Here is what i have found: https://msdn.microsoft.com/en-us/library/system.net.security.sslstream(v=vs.110).aspx

I have several questions:

  1. Do i need to buy an SSL certificate like a certificate for a website ?
  2. Can i generate a self-signed SSL certificate? If so and it's needed can you show me any reference of how to achieve that ?
  3. Is it possible to attach this SSL Stream to my StreamReader/StreamWriter ? Is it even needed or i have to remake the whole connection/communication thing?
  4. When SSLStream is created the whole communication between the server and the client will be encrypted so anybody can't stay in between and listen/understand what they are communicating ?
  5. If i have to attach a certificate file to the client so it may establish a successful SSL connection to the server and then send that client to all the people which have to use that client, doesn't that compromise the security of the SSL connection as i am sending them the certificate as well ?

This is all i wanted to ask. The best thing you can do to help me with your answer is if possible please put the code changes i have to apply in order to make that StreamReader/StreamWriter work over encrypted SSL.

Upvotes: 3

Views: 1493

Answers (1)

Marc Gravell
Marc Gravell

Reputation: 1062770

  1. you need a certificate; makecert would work, but it won't be trusted by default, so you'd either need to supply a custom RemoteCertificateValidationCallback to the consumer, or use a trusted cert; Let's Encrypt might be useful there
  2. makecert or New-SelfSignedCertificate
  3. the SslStream would need to go between the NetworkStream and the StreamReader; frankly, it isn't clear to make that StreamReader give you much here - you could do everything here with just the Streaam API; so instead of WriteLine, you'd encode manually and then Write the encoded buffer; but - there's no reason that you can't just whack SslStream in the middle of the two
  4. yes
  5. only the server needs the certificate (and in particular, only the server needs the private key of the certificate); there is something related called "client certificates" used for authenticating the client, but that is a related but different topic

As an example ripped from SE.Redis and mangled beyond legibility; SE.Redis optionally encrypts, and is basically:

Stream stream = // connect (get a NetworkStream)
if (config.Ssl) {
    var ssl = new SslStream(stream, ... cert check callbacks, etc ...);
    ssl.AuthenticateAsClient(expectedHost, allowedSslProtocols);
    stream = ssl;
}

so it you had something like that, you'd just make sure you attached any StreamReader/StreamWriter after you've wrapped the NetworkStream in SslStream. But again: I really think you could remove StreamReader/StreamWriter here without much inconvenience.

Upvotes: 6

Related Questions