“La conexión subyacente se cerró: se produjo un error inesperado en un envío”. Con certificado SSL

Problema: Obtengo esta excepción “LA CONEXIÓN SUBYACENTE FUE CERRADA: UN ERROR INESPERADO OCURRIDO EN UN ENVÍO” en mis registros y está rompiendo nuestra integración OEM con nuestro sistema de marketing por correo electrónico en momentos aleatorios que varían desde [1 hora – 4 horas]

Mi sitio web está alojado en Windows Server 2008 R2 con IIS 7.5.7600. Este sitio web tiene una gran cantidad de componentes OEM y un tablero integral. Todo funciona bien con todos los otros elementos del sitio web, excepto con uno de nuestro componente de marketing por correo electrónico que estamos utilizando como una solución de marco flotante en nuestro panel. La forma en que funciona es que envío un httpWebRequestobject con todas las credenciales y recibo una URL que pongo en un iframe y funciona. Pero solo funciona durante un tiempo [1 hora – 4 horas] y luego obtengo la siguiente excepción “LA CONEXIÓN SUBYACENTE FUE CERRADA: SE PRODUJO UN ERROR INESPERADO EN UN ENVÍO” e incluso si el sistema intenta obtener la URL de httpWebRequest falla con la misma excepción La única forma de volver a hacerlo funcionar es reciclando el grupo de aplicaciones o todo se edita en web.config. Estoy realmente agotado toda la opción que se me ocurre.

Opción probada

Agregado explícitamente, keep-alive = false

keep-alive = true

Aumentó el tiempo de espera:

He subido esta página a un sitio web que no pertenece a SSL para verificar si el certificado SSL en nuestro servidor de producción está realizando la conexión para indicar cómo.

Cualquier dirección hacia la resolución es muy apreciada.

Código:

 Public Function CreateHttpRequestJson(ByVal url) As String Try Dim result As String = String.Empty Dim httpWebRequest = DirectCast(WebRequest.Create("https://api.xxxxxxxxxxx.com/api/v3/externalsession.json"), HttpWebRequest) httpWebRequest.ContentType = "text/json" httpWebRequest.Method = "PUT" httpWebRequest.ContentType = "application/x-www-form-urlencoded" httpWebRequest.KeepAlive = False 'ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 'TODO change the integratorID to the serviceproviders account Id, useremail Using streamWriter = New StreamWriter(httpWebRequest.GetRequestStream()) Dim json As String = New JavaScriptSerializer().Serialize(New With { _ Key .Email = useremail, _ Key .Chrome = "None", _ Key .Url = url, _ Key .IntegratorID = userIntegratorID, _ Key .ClientID = clientIdGlobal _ }) 'TODO move it to the web.config, Following API Key is holonis accounts API Key SetBasicAuthHeader(httpWebRequest, holonisApiKey, "") streamWriter.Write(json) streamWriter.Flush() streamWriter.Close() Dim httpResponse = DirectCast(httpWebRequest.GetResponse(), HttpWebResponse) Using streamReader = New StreamReader(httpResponse.GetResponseStream()) result = streamReader.ReadToEnd() result = result.Split(New [Char]() {":"})(2) result = "https:" & result.Substring(0, result.Length - 2) End Using End Using Me.midFrame.Attributes("src") = result Catch ex As Exception objLog.WriteLog("Error:" & ex.Message) If (ex.Message.ToString().Contains("Invalid Email")) Then 'TODO Show message on UI ElseIf (ex.Message.ToString().Contains("Email Taken")) Then 'TODO Show message on UI ElseIf (ex.Message.ToString().Contains("Invalid Access Level")) Then 'TODO Show message on UI ElseIf (ex.Message.ToString().Contains("Unsafe Password")) Then 'TODO Show message on UI ElseIf (ex.Message.ToString().Contains("Invalid Password")) Then 'TODO Show message on UI ElseIf (ex.Message.ToString().Contains("Empty Person Name")) Then 'TODO Show message on UI End If End Try End Function Public Sub SetBasicAuthHeader(ByVal request As WebRequest, ByVal userName As [String], ByVal userPassword As [String]) Dim authInfo As String = Convert.ToString(userName) & ":" & Convert.ToString(userPassword) authInfo = Convert.ToBase64String(Encoding.[Default].GetBytes(authInfo)) request.Headers("Authorization") = "Basic " & authInfo End Sub` 

Para mí fue tls12:

 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; 

Si está atrapado con .Net 4.0 y el sitio de destino usa TLS 1.2, necesita la siguiente línea. ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;

fuente: Soporte TLS 1.2 y .NET: cómo evitar errores de conexión

El siguiente código resolvió el problema

 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls Or SecurityProtocolType.Ssl3 

En mi caso, el sitio al que me estoy conectando se ha actualizado a TLS 1.2. Como resultado, tuve que instalar .net 4.5.2 en mi servidor web para poder admitirlo.

Descubrí que esto es una señal de que el servidor en el que está implementando el código tiene instalado un antiguo .NET Framework que no admite TLS 1.1 o TLS 1.2. Pasos para arreglar:

  1. Instalar el .NET Runtime más reciente en sus servidores de producción (IIS y SQL)
  2. Instalar el último .NET Developer Pack en sus máquinas de desarrollo.
  3. Cambie la configuración de “Marco de destino” en sus proyectos de Visual Studio al último .NET Framework.

Puede obtener el último .NET Developer Pack y Runtime desde esta URL: http://getdotnet.azurewebsites.net/target-dotnet-platforms.html

He estado teniendo el mismo problema por días con una integración que también simplemente “solía funcionar antes”.

Fuera de pura depresión, lo intenté

 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Ssl3; 

Esto lo resolvió para mí … aunque la integración solo hace uso estricto de SSLv3.

Llegué a la conclusión de que algo no funcionaba desde que Fiddler informó haber dicho que hay un “cifrado de negociación de TLS vacío” o algo por el estilo.

¡Espero que funcione!

Vaya a su web.config / App.config para verificar qué .NET runtime está usando

     

Aquí está la solución:

  1. .NET 4.6 y superior. No necesita hacer ningún trabajo adicional para admitir TLS 1.2, es compatible de forma predeterminada.

  2. .NET 4.5. TLS 1.2 es compatible, pero no es un protocolo predeterminado. Debes aceptar para usarlo. El siguiente código hará TLS 1.2 por defecto, asegúrese de ejecutarlo antes de hacer una conexión con el recurso seguro:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12

  1. .NET 4.0. No se admite TLS 1.2, pero si tiene instalado .NET 4.5 (o superior) en el sistema, aún puede optar por TLS 1.2, incluso si su marco de aplicación no lo admite. El único problema es que SecurityProtocolType en .NET 4.0 no tiene una entrada para TLS1.2, por lo que tendríamos que usar una representación numérica de este valor enum:

ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;

  1. .NET 3.5 o inferior. TLS 1.2 no es compatible (*) y no hay solución alternativa. Actualice su aplicación a la versión más reciente del marco.

Tuvimos este problema por el cual un sitio web que estaba accediendo a nuestra API obtenía el mensaje “Se cerró la conexión subyacente: se produjo un error inesperado en un mensaje de envío”.

Su código era una mezcla de .NET 3.xy 2.2, que, según tengo entendido, significa que están usando TLS 1.0.

Para que nuestro IIS responda a sus llamadas API, tuvimos que agregar configuraciones de registro en el servidor de IIS para habilitar explícitamente las versiones de TLS. NOTA: debe reiniciar el servidor de Windows (no solo el servicio IIS) después de realizar estos cambios:

 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Client] "DisabledByDefault"=dword:00000000 "Enabled"=dword:00000001 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server] "DisabledByDefault"=dword:00000000 "Enabled"=dword:00000001 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client] "DisabledByDefault"=dword:00000000 "Enabled"=dword:00000001 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server] "DisabledByDefault"=dword:00000000 "Enabled"=dword:00000001 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client] "DisabledByDefault"=dword:00000000 "Enabled"=dword:00000001 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server] "DisabledByDefault"=dword:00000000 "Enabled"=dword:00000001 

Si eso no funciona, también podría experimentar agregando la entrada para SSL 2.0:

 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Client] "DisabledByDefault"=dword:00000000 "Enabled"=dword:00000001 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Server] "DisabledByDefault"=dword:00000000 "Enabled"=dword:00000001 

Para que quede claro, esta no es una buena solución, y la solución correcta es hacer que la persona que llama use TLS 1.2, pero lo anterior puede ayudar a diagnosticar que este es el problema.

Puede acelerar la adición de esas entradas reg con este script de powershell:

 $ProtocolList = @("SSL 2.0","SSL 3.0","TLS 1.0", "TLS 1.1", "TLS 1.2") $ProtocolSubKeyList = @("Client", "Server") $DisabledByDefault = "DisabledByDefault" $Enabled = "Enabled" $registryPath = "HKLM:\\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\" foreach($Protocol in $ProtocolList) { Write-Host " In 1st For loop" foreach($key in $ProtocolSubKeyList) { $currentRegPath = $registryPath + $Protocol + "\" + $key Write-Host " Current Registry Path $currentRegPath" if(!(Test-Path $currentRegPath)) { Write-Host "creating the registry" New-Item -Path $currentRegPath -Force | out-Null } Write-Host "Adding protocol" New-ItemProperty -Path $currentRegPath -Name $DisabledByDefault -Value "0" -PropertyType DWORD -Force | Out-Null New-ItemProperty -Path $currentRegPath -Name $Enabled -Value "1" -PropertyType DWORD -Force | Out-Null } } Exit 0 

Es una versión modificada del script de la página de ayuda de Microsoft para Configurar TLS para VMM . Este artículo de basics.net fue la página que originalmente me dio la idea de ver estos ajustes.

Usar un proxy de depuración HTTP puede causar esto, como Fiddler.

Estaba cargando un certificado PFX desde un archivo local (autenticación a Apple.com) y falló porque Fiddler no pudo pasar este certificado.

Intente desactivar Fiddler para verificar y, si esa es la solución, probablemente deba instalar el certificado en su máquina o de alguna manera que Fiddler pueda usarlo.

El siguiente código resolvió mi problema:

 request.ProtocolVersion = HttpVersion.Version10; // THIS DOES THE TRICK ServicePointManager.Expect100Continue = true; ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;