如何将PFX证书文件应用于SslStream套接字侦听器?

Ork*_*kar 13 c# sockets ssl ssl-certificate pfx

我有一个多线程套接字监听器.它侦听一个端口.它从HTTP网站接收数据并根据其请求发送响应.它运作良好.

现在我想用HTTPS网站做同样的事情.所以我把它改为能够通过SslStream而不是socket来读写.

网站所有者给了我一个pfx文件和一个密码.我将它放入我当地认证商店的可信证书列表中.

认证文件已成功从商店中检索.但在调用AuthenticateAsServer方法后,我的处理程序套接字似乎是空的.所以我在ReceiveCallBack方法中看不到任何数据.

如何将pfx证书应用于我的SslStream套接字?

这是我的代码:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑: 我认为我有一些关于SSL逻辑的问题.PFX文件属于HTTPS网站.但请求来自HTTPS到我的套接字侦听器.然后我的套接字侦听器发送响应.在这种情况下,我是服务器还是客户端?我会使用AuthenticateAsServer还是AuthenticateAsClient?

如果我调用AuthenticateAsServer,它不会抛出错误.它变得经过身份验证没有问题.但我不能通过我的StateObject的SslStream将处理程序套接字中的数据传递给ReceiveCallBack方法.

And*_*ger 4

您可以直接加载 pfx 文件:

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

cert = new X509Certificate2(pfxData, "password", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
Run Code Online (Sandbox Code Playgroud)

如果您从证书存储加载它,那么该密钥对于应用程序是可导出的,这一点很重要。

信息:SSL 3.0 版不再安全。也许这就是你无法建立连接的原因。Chrome 和 Firefox 禁用了该支持。

提示:编写一个简单的 SSL 客户端来发送一些“Hello World”字节。

我通过 ac# 客户端和 chrome 浏览器使用 pfx 格式的自签名证书测试了您的代码。两个客户端都可以连接并发送/接收数据。

我唯一改变的是我将 SSLProtocol 设置为 TLS

stream.AuthenticateAsServer(_cert, false, System.Security.Authentication.SslProtocols.Tls, true);
Run Code Online (Sandbox Code Playgroud)

信息:HTTPS 是基于 tcp/tls 连接的 HTTP 协议。您有一个没有 http 协议的原始 tcp/tls 连接。

提示:不要忘记打开防火墙端口 9002!