2010-09-24 22 views
22

Mi aplicación (.net 3.5 sp1) utiliza HttpWebRequest para comunicarse con diferentes puntos finales, a veces es a través de HTTPS donde cada servidor de alojamiento puede tener un requisito de protocolo de seguridad diferente diga TLS o SSL3 o cualquiera.Establezca SecurityProtocol (Ssl3 o TLS) en .net HttpWebRequest por solicitud

Generalmente los servidores juegan agradable y felizmente negocian/recurren a lo que SecurityProtocol usa TLS o SSL3, pero algunos no y cuando .net está configurado como TLS o SSL3 (el servidor predeterminado) esos servidores que solo admiten SSL3 causa que .net arroje un error de envío.

Por lo que puedo decir .net proporciona el objeto ServicePointManager con una propiedad SecurityProtocol que se puede establecer en TLS, SSL3 o ambos. Por lo tanto, idealmente, cuando se establece en ambos, la idea es que el cliente y el servidor deben negociar qué usar, pero como se indicó anteriormente, no parece funcionar.

Supuestamente podría establecer ServicePointManager.SecurityProtocol = Ssl3, pero ¿qué ocurre con los endpoints que desean usar TLS?

El problema que veo con ServicePointManager y SecurityProtocol es que es estático y, por lo tanto, tiene un dominio de aplicación amplio.

Así que a la pregunta ..

¿cómo iba a ir sobre el uso del SecurityProtocol con un diferente ejemplo HttpWebRequest

1) url 1 configurado para usar TLS | SSL3 (negociar)

2) url 2 fijado a SSL3 (SSL3 solamente)

Respuesta

11

Por desgracia, es imposible parece esto se puede personalizar por punto de servicio. Sugeriría que presente una solicitud de función en el sitio web de MS Connect para esta área.

Como una solución sucia, podría intentar ejecutar los sitios que requieren un protocolo de seguridad diferente en un nuevo dominio de aplicación. Las instancias estáticas son por appdomain, por lo que deberían proporcionarle el aislamiento que necesita.

+0

Gracias por confirmar mis temores, esperaba que ese no fuera el caso. – Rich

+4

Solicitud de MS Connect https://connect.microsoft.com/VisualStudio/feedback/details/605185/cant-set-the-security-protocol-per-servicepoint tiene el estado "no se solucionará" –

+0

@MichaelFreidgeim Si la mira ahora , Microsoft ha respondido explicando que se solucionará en> Net Framework 4.7.1 - * "En la próxima versión de .NET Framework 4.7.1, hemos agregado nuevas propiedades a la clase HttpClientHandler que se usa en System.Net.HttpClient. Esta es la API preferida para usar para las solicitudes HTTP en lugar de HttpWebRequest. Estas nuevas propiedades se agregaron para que coincida con la versión .NET Core 2.0 que ya tiene las propiedades más nuevas en HttpClientHandler. "* – Lankymart

4

Según this answer, la configuración de SecurityProtocol es en realidad por Dominio de aplicación, por lo que podría, si estuviera decidido a hacerlo funcionar, crear diferentes AppDomains para configuraciones separadas, y ordenar sus consultas.

No es exactamente una solución "ordenada", pero podría hacer lo que sea necesario sin recurrir a bibliotecas de terceros.

9

Tuve el mismo problema y escribí la clase de proxy, que abre el puerto en el host local y reenvía todo el tráfico al host: puerto especificado.

así la conexión dice así

[su código] --- HTTP ---> [proxy en localhost: puerto] --- HTTPS ---> [sitio web]

en hecho que puede ser utilizado para envolver cualquier protocolo en SSL/TLS no sólo 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 
     } 
    } 
} 

ejemplo de uso

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

¿Cómo es que esto no ha recibido más votos o ISN? marcó como la respuesta? No he intentado esto todavía, pero @ k010mb0 ¿considerarías esta la respuesta? ¿Cómo funciona esto en una configuración de aplicación web de alto rendimiento? – kstubs

+0

Como mencioné en la respuesta, así es como se resolvió el problema en mi caso. No lo probé para escenarios de alto rendimiento. Ahora está en producción y está estable. Tiene cierta sobrecarga para la conexión adicional a localhost (proxy) y 2 almacenamientos intermedios de tamaño ReceiveBufferSize por conexión, lo que debe tenerse en cuenta para el uso de alta carga. – k010mb0

0

que pueda un cree esto mediante este código para cerrar todas las conexiones subyacentes y forzar un nuevo apretón de manos.

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

Se podría hacer una HttpWebRequest "clase de utilidad" con un método de utilidad para la toma estática HttpWebRequests. En el método de utilidad estática, use la sentencia de bloqueo C# para configurar ServicePointManager.SecurityProtocol y crear una HttpWebRequest particular. La instrucción de bloqueo impide que otros subprocesos del mismo dominio de aplicación ejecuten el mismo código al mismo tiempo, por lo tanto, el protocolo TLS que acaba de establecer no cambiará hasta que se ejecute todo el bloque de bloqueo (= sección crítica).

Pero, para aplicaciones de alto rendimiento (¡extremadamente alto rendimiento!), Esta aplicación podría tener un impacto negativo en el rendimiento.

3

Después de que algunos de nuestros proveedores detuvieron el soporte para ssl3 mientras que otros lo usan exclusivamente, aparecen muchos problemas en nuestro sistema que podrían resolverse con la funcionalidad de esta pregunta. Pero seis años después, todavía no tenemos un mecanismo incorporado para lograr esto. Nuestra solución consiste en definir explícitamente el protocolo de seguridad que admitirá todos los escenarios, como este:

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

Configure todo esto. En mi aplicación, funciona para diferentes protocolos de seguridad.

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

Esto no parece responder a la pregunta de cómo usar diferentes protocolos de seguridad para dos URL diferentes. – Loren

Cuestiones relacionadas