Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to apply PFX certificate file to SslStream socket listener?

I have a multi threaded socket listener. It listens on a port. It receives data from HTTP web sites and sends response according to their request. It works good.

Now I want to do same thing with an HTTPS web site. So I changed it to be able to read and write via SslStream instead of socket.

The web site owner gave me a pfx file and a password. I placed it into the trusted certificates list of my local certification store.

The certification file is retreived from the store successfully. But after calling AuthenticateAsServer method my handler socket seems to be empty. So I cannot see any data in it inside the ReceiveCallBack method.

How can I apply pfx certificate to my SslStream socket?

Here is my code:

using System;
using System.Net;
using System.Net.Sockets;
using System.Net.Security;
using System.Threading;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.IO;

namespace SslStreamTestApp
{
    public class SslStreamSocketListener
    {
        private static readonly ManualResetEvent _manualResetEvent = new ManualResetEvent(false);
        private readonly string pfxSerialNumber = "12345BLABLABLA";

        X509Certificate _cert = null;

        private void GetCertificate()
        {
            X509Store store = new X509Store(StoreName.Root, StoreLocation.CurrentUser);

            store.Open(OpenFlags.ReadOnly);

            X509Certificate2Collection certificateCollection = store.Certificates.Find(X509FindType.FindBySerialNumber, pfxSerialNumber, true);

            if (certificateCollection.Count == 1)
            {
                _cert = certificateCollection[0];
            }
        }

        private void StartListening()
        {
            GetCertificate();

            IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, 9002);

            if (localEndPoint != null)
            {
                Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                if (listener != null)
                {
                    listener.Bind(localEndPoint);
                    listener.Listen(5);

                    Console.WriteLine("Socket listener is running...");

                    listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);
                }
            }
        }

        private void AcceptCallback(IAsyncResult ar)
        {
            _manualResetEvent.Set();

            Socket listener = (Socket)ar.AsyncState;

            Socket handler = listener.EndAccept(ar);

            SslStream stream = new SslStream(new NetworkStream(handler, true));
            stream.AuthenticateAsServer(_cert, false, System.Security.Authentication.SslProtocols.Ssl3, true);

            StateObject state = new StateObject();
            state.workStream = stream;

            stream.BeginRead(state.buffer, 0, StateObject.BufferSize, ReceiveCallback, state);

            listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);
        }

        private void ReceiveCallback(IAsyncResult result)
        {
            StateObject state = (StateObject)result.AsyncState;
            SslStream stream = state.workStream;

            // Check how many bytes received
            int byteCount = stream.EndRead(result);

            Decoder decoder = Encoding.UTF8.GetDecoder();

            char[] chars = new char[decoder.GetCharCount(state.buffer, 0, byteCount)];

            decoder.GetChars(state.buffer, 0, byteCount, chars, 0);

            state.sb.Append(chars);

            string check = state.sb.ToString();

            if (state.sb.ToString().IndexOf("<EOF>") == -1 && byteCount != 0)
            {
                // We didn't receive all data. Continue receiving...
                stream.BeginRead(state.buffer, 0, state.buffer.Length, new AsyncCallback(ReceiveCallback), stream);
            }
            else
            {
                string[] lines = state.sb.ToString().Split('\n');

                // send test message to client
                byte[] test = Encoding.UTF8.GetBytes("\"infoMessage\":\"test\"");
                stream.BeginWrite(test, 0, test.Length, WriteAsyncCallback, stream);
            }
        }

        private void WriteAsyncCallback(IAsyncResult ar)
        {
            SslStream sslStream = (SslStream)ar.AsyncState;

            Console.WriteLine("Sending message to client...");

            try
            {
                sslStream.EndWrite(ar);
            }
            catch (IOException)
            {
                Console.WriteLine("Unable to complete send.");
            }
            finally
            {
                sslStream.Close();
            }
        }
    }

    public class StateObject
    {
        public SslStream workStream = null;
        public const int BufferSize = 1024;
        public byte[] buffer = new byte[BufferSize];
        public StringBuilder sb = new StringBuilder();
    }
}

EDIT: I think I have some problem about SSL logic. PFX file belongs to HTTPS web site. But requests come from HTTPS to my socket listener. Then my socket listener sends response. In this situation, am I server or client? Will I use AuthenticateAsServer or AuthenticateAsClient?

It does not throw error if I call AuthenticateAsServer. It becomes Authenticated with no problem. But I cannot carry the data in handler socket to ReceiveCallBack method via the SslStream of my StateObject.

like image 745
Orkun Bekar Avatar asked Apr 27 '15 11:04

Orkun Bekar


People also ask

How do I import a pfx certificate?

Start Windows Explorer and select and hold (or right-click) the . pfx file, then select Open to open the Certificate Import Wizard. Follow the procedure in the Certificate Import Wizard to import the code-signing certificate into the Personal certificate store.

How do I import pfx into IIS?

pfx file. Note: On the Certificate Store page, select Automatically select the certificate store based on the type of certificate. On the Completing the Certificate Import Wizard page, verify your settings and then, click Finish. You should receive “The import was successful” message.


1 Answers

You can load the pfx file direct with:

byte[] pfxData = File.ReadAllBytes("myfile.pfx");

cert = new X509Certificate2(pfxData, "password", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);

If you are loading it from the cert store its importand that the key is exportable for the application.

Info: SSL version 3.0 is no longer secure. Maybe thats why u cant get a connection. Chrome and Firefox disabled the support.

Tip: Write a simple SSL-Client to send some "Hello World" bytes.

I tested your code with a self signed cert in pfx format over a c# client and the chrome browser. both clients could connect and send/receive data.

the only thing i changed i set the SSLProtocol to TLS

stream.AuthenticateAsServer(_cert, false, System.Security.Authentication.SslProtocols.Tls, true);

Info: HTTPS is the HTTP protocol over a tcp/tls connection. You have a raw tcp/tls connection without the http protocol.

TIP: Dont forget to open the Firewall Port 9002 !

like image 100
Andreas Dirnberger Avatar answered Sep 18 '22 19:09

Andreas Dirnberger