Как сделать HTTPS с TcpClient, как это делает HttpWebRequest?

У меня есть система связи, основанная на TcpClient, и она прекрасно работает, за исключением случаев, когда она делает HTTPS для определенного IP. Тогда это начинает терпеть неудачу.

Используя браузер или HttpWebRequest, у меня нет проблем, делая HTTPS с этим IP.

Я создал тестовую программу, чтобы сузить мою проблему до ее сути, вы можете взглянуть на нее здесь, если хотите: TestViaTcp

Эта тестовая программа отлично работает для базового HTTP на тот же IP, она всегда выдает успешный ответ на запрос. Я помещаю это в цикл, запускаю его нажатием клавиши, и оно будет продолжаться весь день. Как только я переключаю HTTPS, я получаю повторяющийся шаблон. Это сработает, а затем и не сработает, за успехом последуют неудачи, а затем успехи в течение всего дня.

Конкретная ошибка, которую я продолжаю получать, такова:

{"Authentication failed because the remote party has closed the transport stream."}
    [System.IO.IOException]: {"Authentication failed because the remote party has closed the transport stream."}
    Data: {System.Collections.ListDictionaryInternal}
    HelpLink: null
    InnerException: null
    Message: "Authentication failed because the remote party has closed the transport stream."
    Source: "System"
    TargetSite: {Void StartReadFrame(Byte[], Int32, System.Net.AsyncProtocolRequest)}

И вот трассировка стека, прикрепленная к этому:

   at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.ForceAuthentication(Boolean receiveFirst, Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.ProcessAuthentication(LazyAsyncResult lazyResult)
   at System.Net.Security.SslStream.AuthenticateAsClient(String targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, Boolean checkCertificateRevocation)
   at DeriveClassNameSpace.Services.Web.TcpMessaging.TestViaTcp(IPEndPoint endpoint, String auth, Boolean useSSL)

HttpWebRequest и браузер (IIRC) используют библиотеки Win32 для обработки обратной связи, в то время как TcpClient (AFAIK) использует управляемый класс.net Socket, так что я уверен, что между ними есть большая разница. Мне нужно сделать это с TcpClient, поэтому, к сожалению, я не могу просто "использовать HttpWebRequest, так как знаю, что могу заставить его работать".

Самый большой намек на то, в чем здесь проблема, вероятно, это паттерн "работает, не работает, не делает", что вызывает это? Что я могу сделать, чтобы избежать IOException, которое я получаю? Есть ли какой-нибудь способ получить поведение "всегда работает", которое я вижу при выполнении HTTPS с HttpWebRequest?

Должно быть что-то, что я могу сделать с TcpClient, чтобы заставить его действовать и реагировать так же, как это делает HttpWebRequest, но я еще не там. Есть идеи?

Примечание. Сервер, с которым я общаюсь, настраивается в зависимости от того, какой порт он слушает, и какого протокола он ожидает, но в остальном он совершенно не поддается изменению.

Также обратите внимание: я читал, что в.net 3.5 была эта конкретная проблема с SslStream до SP1, но у меня есть SP1, и моя программа построена для 3.5, поэтому я предполагаю, что это не "известная ошибка" I Я сталкиваюсь здесь.

1 ответ

Решение

Разве вы не знаете, после того, как я потрачу время на формирование вопроса, я наткнусь на ответ.

Вот соответствующая документация: запись в блоге jpsanders

Важной частью было это:

Если в стеке исключения есть что-то похожее на это: System.IO.IOException: аутентификация не удалась, потому что удаленная сторона закрыла транспортный поток. Возможно, это более старый сервер, который не понимает TLS, поэтому вам необходимо изменить его, как указано в 915599 КБ, на что-то вроде этого: ServicePointManager.SecurityProtocol= SecurityProtocolType.Ssl3; Перед тем, как выполнять какие-либо HTTPS-вызовы в приложении.

Поэтому я изменяю свои принятые протоколы на это: (исключая возможность TLS)

SslProtocols protocol = SslProtocols.Ssl2 | SslProtocols.Ssl3;

И все отлично работает.
Я разрешил TLS, поэтому сначала он пытается это сделать, а сервер его не поддерживает, поэтому поток закрывается. В следующий раз он использует Ssl2 или Ssl3, и все в порядке. Или что-то типа того. Это работает, я счастливая панда.

Другие вопросы по тегам