У меня есть многопоточный прослушиватель сокетов. Он прослушивает порт. Он получает данные с веб-сайтов 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.
Есть ли причина, по которой вы начинаете получать на самом обработчике, а не на 'SslStream stream'? В любом случае отправитель отправляет зашифрованные данные. – Caramiriel
Я не хочу вносить большие изменения в свой код, потому что он работает хорошо. Итак, я ищу, если есть способ использования сокета для HTTPS. –
Я не эксперт на транспорте низкого уровня, но я думаю, это невозможно.Невозможно ожидать чтения «сырого» сокета и получения открытого текста при получении, если клиент использует SSL. Вам понадобится что-то, чтобы разобрать/декодировать поток SSL (для чего нужен 'SslStream'). Вы можете адаптировать существующий код для использования 'Stream' /' NetworkStream'. Тогда 'SslStream' должно быть довольно легко. – Caramiriel