Подтвердить что ты не робот

Как применить файл сертификата PFX к прослушивателю сокета SslStream?

У меня есть многопоточный прослушиватель сокетов. Он прослушивает порт. Он получает данные с веб-сайтов HTTP и отправляет ответ в соответствии с их запросом. Он работает хорошо.

Теперь я хочу сделать то же самое с веб-сайтом HTTPS. Поэтому я изменил его, чтобы читать и писать через SslStream вместо сокета.

Владелец веб-сайта дал мне файл 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();
    }
}

EDIT: Я думаю, что у меня есть некоторые проблемы с логикой SSL. Файл PFX принадлежит веб-сайту HTTPS. Но запросы поступают от HTTPS к моему слушателю сокета. Затем мой прослушиватель сокетов посылает ответ. В этой ситуации я сервер или клиент? Использую ли я AuthenticateAsServer или AuthenticateAsClient?

Он не выдает ошибку, если я вызываю AuthenticateAsServer. Он становится аутентифицированным без проблем. Но я не могу переносить данные в дескриптор обработчика в метод ReceiveCallBack через SslStream моего объекта StateObject.

4b9b3361

Ответ 1

Вы можете напрямую загрузить файл pfx:

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

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

Если вы загружаете его из хранилища сертификатов, его импорт и ключ экспортируется для приложения.

Информация: версия SSL 3.0 больше не защищена. Возможно, поэтому у вас нет связи. Chrome и Firefox отключили поддержку.

Совет. Напишите простой SSL-клиент для отправки некоторых байтов Hello World.

Я проверил ваш код с самоподписанным сертификатом в формате pfx над клиентом С# и браузером Chrome. оба клиента могли подключать и отправлять/получать данные.

Единственное, что я изменил, я установил SSLProtocol в TLS

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

Информация: HTTPS - это протокол HTTP по подключению tcp/tls. У вас есть необработанное соединение tcp/tls без протокола http.

СОВЕТ. Не забудьте открыть порт Firewall 9002!