Установите SecurityProtocol (Ssl3 или TLS) в .net HttpWebRequest для каждого запроса. - PullRequest
29 голосов
/ 25 сентября 2010

Мое приложение (.net 3.5 sp1) использует HttpWebRequest для связи с различными конечными точками, иногда через HTTPS, где у каждого хост-сервера могут быть разные требования к протоколу безопасности, например, TLS или SSL3, или и то и другое.

Как правило, серверы играют хорошо и с удовольствием договариваются / отказываются от того, какой SecurityProtocol использует TLS или SSL3, но некоторые этого не делают, и когда .net настроен как TLS или SSL3 (по умолчанию, я думаю), те серверы, которые поддерживают только SSL3 Заставьте .net выдать ошибку отправки.

Из того, что я могу сказать, .net предоставляет объекту ServicePointManager свойство SecurityProtocol, для которого можно установить TLS, SSL3 или оба. Следовательно, в идеале, когда задано и то и другое, идея заключается в том, что клиент и сервер должны договариваться о том, что использовать, но, как уже было сказано, это не работает.

Возможно, вы могли бы установить ServicePointManager.SecurityProtocol = Ssl3, но как насчет конечных точек, которые хотят использовать TLS?

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

Так к вопросу ..

как мне использовать HttpWebRequest с другим протоколом безопасности, например,

1) URL 1 настроен на использование TLS | Ssl3 (переговоры)

2) url 2 установлен на Ssl3 (только Ssl3)

Ответы [ 9 ]

16 голосов
/ 26 сентября 2010

К сожалению, не похоже, что вы можете настроить это для каждой точки обслуживания.Я бы посоветовал вам подать запрос функции на веб-сайте MS Connect для этой области.

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

13 голосов
/ 05 ноября 2014

У меня возникла та же проблема, и я написал прокси-класс, который открывает порт на localhost и перенаправляет весь трафик на указанный хост: порт.

, поэтому соединение идет следующим образом

[ваш код]--- HTTP ---> [прокси на локальном хосте: порт] --- HTTPS ---> [веб-сайт]

фактически его можно использовать для включения любого протокола в SSL / TLS, а не только в HTTP

using System;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;

namespace System
{
    class sslProxy : IDisposable
    {
        readonly string host;
        readonly int port;
        readonly TcpListener listener;
        readonly SslProtocols sslProtocols;
        bool disposed;
        static readonly X509CertificateCollection sertCol = new X509CertificateCollection();
        public sslProxy(string url, SslProtocols protocols)
        {
            var uri = new Uri(url);
            host = uri.Host;
            port = uri.Port;
            sslProtocols = protocols;
            listener = new TcpListener(IPAddress.Loopback, 0);
            listener.Start();
            listener.BeginAcceptTcpClient(onAcceptTcpClient, null);
            Proxy = new WebProxy("localhost", (listener.LocalEndpoint as IPEndPoint).Port);
        }
        public WebProxy Proxy
        {
            get;
            private set;
        }
        class stBuf
        {
            public TcpClient tcs;
            public TcpClient tcd;
            public Stream sts;
            public Stream std;
            public byte[] buf;
            public stBuf dup;
        }
        void onAcceptTcpClient(IAsyncResult ar)
        {
            if (disposed) return;
            var tcl = listener.EndAcceptTcpClient(ar);
            TcpClient tcr = null;
            try
            {
                listener.BeginAcceptTcpClient(onAcceptTcpClient, null);
                var nsl = tcl.GetStream();

                tcr = new TcpClient(host, port);
                Stream nsr = tcr.GetStream();
                if (sslProtocols != SslProtocols.None)
                {
                    var sss = new SslStream(nsr, true);
                    sss.AuthenticateAsClient(host, sertCol, sslProtocols, false);
                    nsr = sss;
                } // if

                var sts = new stBuf() { tcs = tcl, sts = nsl, tcd = tcr, std = nsr, buf = new byte[tcl.ReceiveBufferSize] };
                var std = new stBuf() { tcs = tcr, sts = nsr, tcd = tcl, std = nsl, buf = new byte[tcr.ReceiveBufferSize] };
                sts.dup = std;
                std.dup = sts;

                nsl.BeginRead(sts.buf, 0, sts.buf.Length, onReceive, sts);
                nsr.BeginRead(std.buf, 0, std.buf.Length, onReceive, std);
            } // try
            catch
            {
                tcl.Close();
                if (tcr != null) tcr.Close();
            } // catch
        }
        void close(stBuf st)
        {
            var dup = st.dup;
            if (dup != null)
            {
                dup.dup = st.dup = null;
                st.sts.Dispose();
                st.std.Dispose();
            } // if
        }
        void onReceive(IAsyncResult ar)
        {
            var st = ar.AsyncState as stBuf;
            try
            {
                if (!(st.dup != null && st.tcs.Connected && st.sts.CanRead && !disposed)) { close(st); return; };
                var n = st.sts.EndRead(ar);
                if (!(n > 0 && st.tcd.Connected && st.std.CanWrite)) { close(st); return; };
                st.std.Write(st.buf, 0, n);
                if (!(st.tcs.Connected && st.tcd.Connected && st.sts.CanRead && st.std.CanWrite)) { close(st); return; };
                st.sts.BeginRead(st.buf, 0, st.buf.Length, onReceive, st);
            } // try
            catch
            {
                close(st);
            } // catch
        }
        public void Dispose()
        {
            if (!disposed)
            {
                disposed = true;
                listener.Stop();
            } // if
        }
    }
}

пример использования

// create proxy once and keep it
// note you have to mention :443 port (https default)
// ssl protocols to use (enum can use | or + to have many)
var p = new sslProxy("http://www.google.com:443", SslProtocols.Tls);
// using our connections
for (int i=0; i<5; i++)
{
    // url here goes without https just http
    var rq = HttpWebRequest.CreateHttp("http://www.google.com/") as HttpWebRequest;
    // specify that we are connecting via proxy
    rq.Proxy = p.Proxy;
    var rs = rq.GetResponse() as HttpWebResponse;
    var r = new StreamReader(rs.GetResponseStream()).ReadToEnd();
    rs.Dispose();
} // for
// just dispose proxy once done
p.Dispose();
6 голосов
/ 20 февраля 2017

После того, как некоторые из наших поставщиков прекратили поддержку ssl3, в то время как другие используют ее исключительно, в нашей системе появилось много проблем, которые можно было бы решить с помощью функциональности из этого вопроса.Но шесть лет спустя у нас все еще нет встроенного механизма для достижения этой цели.Наш обходной путь заключается в явном определении протокола безопасности, который будет поддерживать все сценарии, например:

    System.Net.ServicePointManager.SecurityProtocol = 
    System.Net.SecurityProtocolType.Ssl3 
    | System.Net.SecurityProtocolType.Tls12 
    | SecurityProtocolType.Tls11 
    | SecurityProtocolType.Tls;
4 голосов
/ 15 апреля 2011

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

Не совсем "аккуратное" решение, но может просто сделать то, что вам нужно, без обращения к сторонним библиотекам.

1 голос
/ 28 августа 2018

В Net 4.6 есть HttpClient и WinHttpHandler пакет nuget для Windows (от Microsoft) для установки SslProtocols параметров. С ядром Net вы можете использовать HttpClientHandler класс для того же.

using (var hc = new HttpClient(new WinHttpHandler() // should have it as a static member
{
    AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip,
    SslProtocols = SslProtocols.Tls | 
                   SslProtocols.Tls11 | 
                   SslProtocols.Tls12
}))
{
    var r = hc.SendAsync(new HttpRequestMessage(HttpMethod.Get, "https://..."));
    r.Wait();
    Console.WriteLine(r.Result.StatusCode);
} // using
1 голос
/ 19 июня 2015

Вы можете достичь этого с помощью этого кода, чтобы закрыть все базовые соединения и вызвать новое рукопожатие.

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
...
...
...
request.ServicePoint.CloseConnectionGroup(request.ConnectionGroupName);
0 голосов
/ 12 июля 2018

Я знаю, что этот вопрос старый, но проблема сохраняется даже в .Net 4.7.2. В моем случае у меня есть многопоточное приложение, которое взаимодействует с двумя конечными точками. Одна конечная точка работает только с TLS 1.2, а другая конечная точка работает только с TLS 1.0 (команда, ответственная за это, работает над исправлением своей конечной точки, поэтому она также будет поддерживать TLS 1.2).

Чтобы обойти это, я переместил вызовы службы для конечной точки, которая работает только с TLS 1.0, в отдельный класс в той же сборке, а затем загрузил сборку в отдельный домен приложения. Делая это, я могу использовать эту глобальную переменную:

System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls; 

только для вызовов к сломанной конечной точке, в то же время продолжая работать и с вызовами к конечной точке TLS 1.2 (для которых не требуется устанавливать ServicePointManager.SecurityProtocol что-либо конкретное). Это также гарантирует, что когда хорошая конечная точка будет обновлена ​​до TLS 1.3, мне не нужно будет повторно выпускать мое приложение. Мое приложение является многопоточным и имеет большую емкость, поэтому блокировка или попытка / окончание не являются адекватными решениями.

Вот код, который я использовал для загрузки сборки в отдельный домен. Обратите внимание, что я загружаю сборку из ее текущего местоположения (Aspnet Tempfiles), чтобы она не блокировала сборку в каталоге bin и не блокировала будущие развертывания.

Также обратите внимание, что прокси-класс наследует MarshalByRefObject, поэтому он используется в качестве прозрачного прокси-сервера, который сохраняет System.Net.ServicePointManager со своим собственным значением в собственном AppDomain.

Это кажется глупым ограничением со стороны .Net Framework, я хотел бы, чтобы мы могли просто указать протокол непосредственно в веб-запросе, а не прыгать через обручи, особенно после многих лет этого. (

Этот код работает, надеюсь, он вам поможет! :)

private static AppDomain _proxyDomain = null;
private static Object _syncObject = new Object();

public void MakeACallToTls10Endpoint(string tls10Endpoint, string jsonRequest)
{
   if (_proxyDomain == null)
   {
      lock(_syncObject) // Only allow one thread to spin up the app domain.
      {
         if (_proxyDomain == null)
         {
            _proxyDomain = AppDomain.CreateDomain("CommunicationProxyDomain");
         }
      }
   }

   Type communicationProxyType = typeof(CommunicationProxy);
   string assemblyPath = communicationProxyType.Assembly.Location;

   // Always loading from the current assembly, sometimes this moves around in ASPNet Tempfiles causing type not found errors if you make it static.
   ObjectHandle objectHandle = _proxyDomain.CreateInstanceFrom(assemblyPath, communicationProxyType.FullName.Split(',')[0]);
   CommunicationProxy communicationProxy = (CommunicationProxy)objectHandle.Unwrap();

   return communicationProxy.ExecuteHttpPost(tls10Endpoint, jsonRequest);
}

Затем в отдельном классе:

[Serializable]
public class CommunicationProxy : MarshalByRefObject
{
   public string ExecuteHttpPost(string tls10Endpoint, string jsonRequest)
   {
      ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;

      // << Bunch of code to do the request >>
   }
}
0 голосов
/ 21 января 2018

Установите все это. В моем приложении это работа для разных протоколов безопасности.

System.Net.ServicePointManager.SecurityProtocol = 
System.Net.SecurityProtocolType.Ssl3 
| System.Net.SecurityProtocolType.Tls12 
| SecurityProtocolType.Tls11 
| SecurityProtocolType.Tls;
0 голосов
/ 25 июля 2016

Вы можете создать «служебный класс» HttpWebRequest с помощью статического служебного метода для выполнения запросов HttpWebRequest. В методе статической утилиты используйте оператор c # lock для настройки ServicePointManager.SecurityProtocol и создания определенного HttpWebRequest. Оператор блокировки не позволяет другим потокам из того же AppDomain выполнять тот же код в одно и то же время, поэтому только что установленный протокол TLS не изменится, пока не будет выполнен весь блок блокировки (= критическая секция).

Но помните, что для действительно высокопроизводительных приложений (чрезвычайно высокопроизводительных!) Этот подход может оказать негативное влияние на производительность.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...