Set the SecurityProtocol (Ssl3 or TLS) on the .net HttpWebRequest per request

前端 未结 9 1232
执笔经年
执笔经年 2020-11-28 07:10

My application (.net 3.5 sp1) uses the HttpWebRequest to communicate with different endpoints, sometimes its over HTTPS where each hosting server may have a different securi

9条回答
  •  小蘑菇
    小蘑菇 (楼主)
    2020-11-28 07:30

    I had the same issue and wrote proxy class, which opens port on localhost and forwards all traffic to specified host:port.

    so the connection goes like this

    [your code] --- HTTP ---> [proxy on localhost:port] --- HTTPS ---> [web site]

    in fact it can be used to wrap any protocol into SSL/TLS not just 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
            }
        }
    }
    

    usage example

    // 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();
    

提交回复
热议问题