net example asp .net httpwebrequest

example - Establezca el SecurityProtocol(Ssl3 o TLS) en.net HttpWebRequest por solicitud



post c# asp net (9)

Mi aplicación (.net 3.5 sp1) usa HttpWebRequest para comunicarse con diferentes puntos finales, a veces es sobre HTTPS, donde cada servidor de alojamiento puede tener un requisito de protocolo de seguridad diferente, como TLS o SSL3 o cualquiera.

En general, los servidores juegan bien y negocian felizmente / retroceden en qué SecurityProtocol usar TLS o SSL3, pero algunos no lo hacen y cuando .net se configura como TLS o SSL3 (el valor predeterminado, creo) esos servidores que solo admiten SSL3 causan .net para lanzar un error de envío.

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

Supuestamente, podría establecer el ServicePointManager.SecurityProtocol = Ssl3, pero ¿qué pasa con los puntos finales que desean usar TLS?

El problema que veo con el ServicePointManager y el SecurityProtocol es que su dominio estático y, por lo tanto, se aplica a toda la aplicación.

Así que a la pregunta ..

¿Cómo podría utilizar HttpWebRequest con un SecurityProtocol diferente, por ejemplo?

1) url 1 set para usar TLS | Ssl3 (negociar)

2) url 2 establecido en Ssl3 (solo Ssl3)


A partir de la Red 4.5, hay un paquete HttpClient y WinHttpHandler nuget disponible para Windows (de Microsoft) para configurar los parámetros de SslProtocols . Puede usar la clase HttpClientHandler para net core.

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


Desafortunadamente, no parece que pueda personalizar esto por punto de servicio. Le sugiero que presente una solicitud de función en el sitio web de MS Connect para esta área.

Como solución alternativa, puede 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ía darle el aislamiento que necesita.


Después de que algunos de nuestros proveedores interrumpieron el soporte para ssl3 mientras que otros lo usan exclusivamente, muchos problemas aparecen 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 es definir explícitamente el protocolo de seguridad que admita todos los escenarios, como este:

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


Establecer todo esto En mi aplicación es trabajo para diferentes protocolos de seguridad.

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


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

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


Puede lograr esto con este código para cerrar todas las conexiones subyacentes y forzar un nuevo saludo.

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


Sé que esta pregunta es antigua, pero el problema permanece incluso con .Net 4.7.2. En mi caso, tengo una aplicación de subprocesos múltiples que está hablando con dos puntos finales. Un punto extremo solo funciona con TLS 1.2, y el otro punto extremo solo funciona con TLS 1.0 (el equipo responsable de ese equipo está trabajando para corregir su punto final, por lo que también es compatible con TLS 1.2).

Para solucionar esto, moví las llamadas de servicio para el punto final que solo funciona con TLS 1.0 a una clase separada en el mismo ensamblaje, y luego cargué el ensamblaje en un dominio de aplicación separado. Al hacer esto, puedo usar esta variable global:

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

solo para las llamadas a la terminal rota, mientras que las llamadas a la terminal TLS 1.2 (que no requieren la configuración de ServicePointManager.SecurityProtocol como algo específico) continúan funcionando. Esto también garantiza que cuando se actualice el buen punto final a TLS 1.3 no necesito volver a lanzar mi aplicación. Mi aplicación es multihebra y de gran capacidad, por lo que el bloqueo o la prueba / finalmente no son soluciones adecuadas.

Aquí está el código que utilicé para cargar el ensamblaje en un dominio separado. Tenga en cuenta que cargo el ensamblaje desde su ubicación actual (Aspnet Tempfiles) para que no bloquee el ensamblaje en el directorio bin y bloquee las implementaciones futuras.

Además, tenga en cuenta que la clase proxy hereda MarshalByRefObject para que se utilice como un proxy transparente que conserva System.Net.ServicePointManager con su propio valor en su propio dominio de aplicación.

Esto parece una limitación tonta por parte del marco .Net. Desearía que pudiéramos simplemente especificar el Protocolo directamente en la solicitud web en lugar de saltar a través de aros, especialmente después de años de esto. :(

Este código funciona, espero que te ayude! :)

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); }

Luego, en una clase separada:

[Serializable] public class CommunicationProxy : MarshalByRefObject { public string ExecuteHttpPost(string tls10Endpoint, string jsonRequest) { ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls; // << Bunch of code to do the request >> } }


Según esta respuesta , la configuración de SecurityProtocol es en realidad por cada dominio de aplicación, por lo que podría, si estuviera decidido a hacer que funcione, cree un dominio de aplicación por separado para configuraciones por separado y marque sus consultas.

No es exactamente una solución "clara", pero puede hacer posible lo que necesita sin tener que recurrir a bibliotecas de terceros.


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

así que la conexión es así

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

de hecho, se puede usar para envolver cualquier protocolo en SSL / TLS no solo 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();