"Se cerró la conexión subyacente: se produjo un error inesperado en un envío". Con certificado SSL

119

Problema: aparece esta excepción "LA CONEXIÓN SUBYACENTE SE CERRÓ: OCURRIÓ UN ERROR INESPERADO EN UN ENVÍO" en mis registros y está interrumpiendo nuestra integración OEM con nuestro sistema de marketing por correo electrónico en momentos aleatorios que varían de [1 hora a 4 horas]

Mi sitio web está alojado en un servidor Windows 2008 R2 con IIS 7.5.7600. Este sitio web tiene una gran cantidad de componentes OEM y un panel completo. Todo funciona bien con todos los demás elementos del sitio web, excepto con uno de nuestros componentes de marketing por correo electrónico que estamos utilizando como una solución de iframe dentro de nuestro panel de control. La forma en que funciona es, envío un httpWebRequestobject con todas las credenciales y obtengo una URL que puse 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: OCURRIÓ 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 hacerlo funcionar de nuevo es reciclar el grupo de aplicaciones o cualquier cosa se edita en web.config.

Opción probada

Agregado explícitamente, keep-alive = false

keep-alive = true

Aumentó el tiempo de espera: <httpRuntime maxRequestLength="2097151" executionTimeout="9999999" enable="true" requestValidationMode="2.0" />

He subido esta página a un sitio web que no es SSL para comprobar si el certificado SSL en nuestro servidor de producción está haciendo que la conexión se caiga de alguna manera.

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

Codigo:

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`
Arvind Morwal
fuente
¿Alguna vez resolviste esto?
Brett G
11
sí, pude hacerlo funcionar con este código ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls o SecurityProtocolType.Ssl3
Arvind Morwal
Estuve a punto de morir por el mismo problema. Me tomó varias horas luchar con el mismo problema. Gracias por tus comentarios, me salvó el día.
Sameers Javed
2
@ user3458212 debe agregar su comentario como respuesta
icc97
2
En mi caso, al ejecutar el sitio web en Visual Studio 15 todo va bien, pero al final, debido a que no puedo actualizar el marco en el servidor, y forzar TLS 1.2 y deshabilitar el mantenimiento de vida no funciona, tuve que configurar un intermediario servidor web como proxy del servidor web de destino que interrumpe la conexión.
José Roberto García Chico

Respuestas:

193

Para mí fue tls12:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
Lanklaas
fuente
42
Tenga en cuenta que debe tener cuidado porque este cambio es global para su AppDomain y hará que fallen las llamadas a cualquier sitio que no ofrezca TLS 1.2 (lo que puede preferir si los datos que se van a transportar son realmente confidenciales). Para preferir TLS 1.2 pero aún permitir 1.1 y 1.0, debe usarlos en OR:ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
Dusty
Lo mismo aquí, me salvaste la vida, pasaste tanto tiempo para descubrir qué estaba mal con RestSharp
darul75
Esta solución también funciona para aquellos que no utilizan RestSharp, así como para aquellos que no utilizan ServicePointManager. Simplemente copie y pegue la línea anterior antes de su llamada WebRequest o lo que esté usando para realizar la solicitud. Inicialmente ignoré esta solución debido a las razones anteriores.
goku_da_master
o simplemente agréguelo a lo que ya está allí ... System.Net.ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
uosjead
8
Para hacer esto en PowerShell, "binario o" juntos así:[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls
Adam S
62

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

fuente: Compatibilidad con TLS 1.2 y .NET: Cómo evitar errores de conexión

Tochi
fuente
5
¡Increíble! Solo agregaría que (SecurityProtocolType)768se puede usar para "Tls11" (es decir, TLS 1.1).
Solomon Rutzky
2
Esto realmente ayuda. Me salvó el día. Tengo que seguir con .Net 2.0.
Hao Nguyen
22

El siguiente código resolvió el problema

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls Or SecurityProtocolType.Ssl3
Arvind Morwal
fuente
5
Esto funcionaría, sin embargo, tenga en cuenta que ServicePointManager.SecurityProtocoles un objeto estático, lo que significa que cambiar este valor afectará a todas las subsecuencias WebRequesto WebClientllamadas. Puede crear por separado AppDomainsi desea ServicePointManagertener diferentes configuraciones. Consulte stackoverflow.com/questions/3791629/… para obtener más detalles.
stack247
1
Lectura adicional útil para comprender lo que hace este código: stackoverflow.com/questions/26389899/…
Jon Schneider
@Marnee Lo puse en la raíz de composición de mi aplicación, por lo que se establece antes de que ocurra cualquier E / S
JG en SD
@Marnee Ponlo en un constructor estático, para que se ejecute exactamente una vez, la primera vez que se accede a la clase. Sin embargo, tuve que habilitar todos los protocolos para cubrir todos los casos.
Nyerguds
14

He tenido el mismo problema durante días con una integración que también "solía funcionar antes".

Por pura depresión, lo intenté

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

Esto me resolvió ... aunque la integración estrictamente solo hace uso de SSLv3.

Me di cuenta de que algo estaba mal desde que Fiddler informó que había un "cifrado de negociación TLS vacío" o algo por el estilo.

¡Ojalá funcione!

Medismal
fuente
Tenga en cuenta que, incluso si su código no necesita TLS, si el servidor con el que se está comunicando SÍ, intentará negociar con TLS y fallará si no puede. El cifrado de negociación TLS vacío era la ranura que esperaba el intercambio de protocolo TLS que finalmente proporcionó. Probablemente "solía funcionar antes" porque el administrador del servidor probablemente acababa de habilitar TLS en el servidor con el que se comunicaba su aplicación.
vapcguy
13

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 admitirlo.

aboy021
fuente
¿Se puede hacer esto a nivel de registro en la máquina? He desactivado todos los protocolos SSL, dejando TLS 1.0, 1.1, 1.2, sin embargo, tengo entendido que cualquier cosa menos que TLS 1.2 debe eliminarse pronto para cumplir con PCI.
brendo234
13

Vaya a su web.config / App.config para verificar qué tiempo de ejecución .net está utilizando

  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
  </startup>

Esta es la solucion:

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

  2. .NET 4.5. Se admite TLS 1.2, pero no es un protocolo predeterminado. Debes suscribirte para usarlo. El siguiente código hará que TLS 1.2 sea el predeterminado, asegúrese de ejecutarlo antes de realizar una conexión a un recurso seguro:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12

  1. .NET 4.0. TLS 1.2 no es compatible, pero si tiene .NET 4.5 (o superior) instalado en el sistema, puede optar por TLS 1.2 incluso si el marco de su 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 de enumeración:

ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;

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

Descubrí que esto es una señal de que el servidor donde está implementando el código tiene instalado un marco .NET antiguo que no es compatible con TLS 1.1 o TLS 1.2. Pasos para solucionarlo:

  1. Instalación del último tiempo de ejecución .NET en sus servidores de producción (IIS y SQL)
  2. Instalar el último paquete de desarrollador de .NET en sus máquinas de desarrollo.
  3. Cambie la configuración de "Marco de destino" en sus proyectos de Visual Studio al marco .NET más reciente.

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

Patricio
fuente
Se cambió el marco de destino de 4.5.2 a 4.6.1 y comencé a trabajar, gracias Patrick.
Vivek Sharma
4

Tuvimos este problema por el cual un sitio web que estaba accediendo a nuestra API recibía el mensaje "La conexión subyacente se cerró: se produjo un error inesperado en un envío". mensaje.

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

La respuesta a continuación puede ayudarlo a diagnosticar el problema habilitando TLS 1.0, SSL 2 y SSL3, pero para ser muy claro, no desea hacerlo a largo plazo, ya que los tres protocolos se consideran inseguros y ya no deberían serlo. utilizado :

Para que nuestro IIS responda a sus llamadas a la API, tuvimos que agregar configuraciones de registro en el servidor de IIS para habilitar explícitamente 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 puede 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 ser claros, 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 de registro 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

Esa 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 mirar estas configuraciones.

tomRedox
fuente
Nuestro problema fue en torno a un canal de lanzamiento a través de Team City que se detuvo repentinamente cuando cambiamos el certificado para su servidor en vivo. Ya habíamos cambiado el servidor para usar solo TLS1.2 y nuestra canalización de Team City dejó de funcionar ... Funcionó como un sueño ... agregué las entradas de registro y reinició el servidor ... BOOM !!! Gracias tomRedox !!
Gwasshoppa
@Gwasshoppa, solo para reiterar que lo anterior es solo una solución provisional para diagnosticar el problema. Ahora que sabe que es un problema de la versión TLS, la solución es cambiar la canalización de la versión para que pueda funcionar con TLS1.2 y luego apagar TLS <1.2 y SSL 2 y 3 nuevamente. Actualicé ligeramente la respuesta anterior para enfatizar eso también.
tomRedox
4

Solo agrega:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

M.Qudah
fuente
1
Proporcione una explicación con su respuesta.
Word Rearranger
4
Esta respuesta tampoco agrega nada a las anteriores.
Arvindh Mani
2

Si ayuda a alguien, el nuestro fue un problema con un certificado faltante. El entorno es Windows Server 2016 Standard con .Net 4.6.

Hay un URI https de servicio WCF autohospedado, para el cual Service.Open () se ejecutaría sin errores. Otro hilo seguiría accediendo a https: // OurIp: 443 / OurService? Wsdl para asegurarse de que el servicio estuviera disponible. Acceder al WSDL solía fallar con:

Se cerró la conexión subyacente: se produjo un error inesperado en un envío.

El uso de ServicePointManager.SecurityProtocol con la configuración aplicable no funcionó. Jugar con las funciones y funciones del servidor tampoco ayudó. Luego intervino Jaise George , el SE, resolviendo el problema en un par de minutos. Jaise instaló un certificado autofirmado en IIS, solucionando el problema. Esto es lo que hizo para abordar el problema:

(1) Abra el administrador de IIS (inetmgr) (2) Haga clic en el nodo del servidor en el panel izquierdo y haga doble clic en "Certificados de servidor". (3) Haga clic en "Crear certificado autofirmado" en el panel derecho y escriba lo que desee para el nombre descriptivo. (4) Haga clic en "Sitio web predeterminado" en el panel izquierdo, haga clic en "Enlaces" en el panel derecho, haga clic en "Agregar", seleccione "https", seleccione el certificado que acaba de crear y haga clic en "Aceptar" (5) Acceso la URL https, debe ser accesible.

DiligentKarma
fuente
¡Sin embargo, habría pensado que el administrador del servidor habría agregado el certificado SSL! ¡Oh! lol :) Sin embargo, puedo ver que esto está sucediendo, o un certificado vencido que necesita renovarse probablemente sería el escenario más aplicable.
vapcguy
2

Simplemente cambie la versión de su aplicación como 4.0 a 4.6 y publique ese código.

También agregue las siguientes líneas de código:

httpRequest.ProtocolVersion = HttpVersion.Version10; 
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
Viresh
fuente
1

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

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

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

Simon_Weaver
fuente
0

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;
Coronel Software
fuente