2015-04-27 4 views
13

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

Теперь я хочу сделать то же самое с веб-сайтом 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.

+0

Есть ли причина, по которой вы начинаете получать на самом обработчике, а не на 'SslStream stream'? В любом случае отправитель отправляет зашифрованные данные. – Caramiriel

+0

Я не хочу вносить большие изменения в свой код, потому что он работает хорошо. Итак, я ищу, если есть способ использования сокета для HTTPS. –

+0

Я не эксперт на транспорте низкого уровня, но я думаю, это невозможно.Невозможно ожидать чтения «сырого» сокета и получения открытого текста при получении, если клиент использует SSL. Вам понадобится что-то, чтобы разобрать/декодировать поток SSL (для чего нужен 'SslStream'). Вы можете адаптировать существующий код для использования 'Stream' /' NetworkStream'. Тогда 'SslStream' должно быть довольно легко. – Caramiriel

ответ

1

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

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

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

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

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

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

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

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

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

Info: HTTPS это протокол HTTP через соединение TCP/TLS. У вас есть необработанное соединение tcp/tls без протокола http.

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

+0

Как я могу сделать его доступным для экспорта? Он хранится в доверенных издателях хранилища сертификатов. Я открыл его через сертификационный магазин, и я добавил его к решению и попытался открыть его прямо оттуда, но я не мог этого добиться. Я попробовал Tls вместо SSL3, но не помог. –

+0

Вы можете проверить для privatekey: if (! Cert.HasPrivateKey) throw new Exception («закрытый ключ не загружен»); Я проведу ваш код через несколько часов. –