Установите SecurityProtocol (Ssl3 или TLS) в .net HttpWebRequest за запрос

В моем приложении (.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 с другим SecurityProtocol, например.

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

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

Ответ 1

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

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

Ответ 2

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

поэтому соединение будет выглядеть следующим образом:

[ваш код] --- HTTP --- > [прокси на localhost: порт] --- 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();

Ответ 3

В соответствии с этим ответом параметр SecurityProtocol фактически является per-AppDomain, поэтому вы могли бы, если бы решились заставить его работать, создать отдельные AppDomains для отдельных настроек, и сортируйте свои запросы через.

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

Ответ 4

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

    System.Net.ServicePointManager.SecurityProtocol = 
    System.Net.SecurityProtocolType.Ssl3 
    | System.Net.SecurityProtocolType.Tls12 
    | SecurityProtocolType.Tls11 
    | SecurityProtocolType.Tls;

Ответ 5

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

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
...
...
...
request.ServicePoint.CloseConnectionGroup(request.ConnectionGroupName);

Ответ 6

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

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

Ответ 7

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

System.Net.ServicePointManager.SecurityProtocol = 
System.Net.SecurityProtocolType.Ssl3 
| System.Net.SecurityProtocolType.Tls12 
| SecurityProtocolType.Tls11 
| SecurityProtocolType.Tls;