“A conexão subjacente foi fechada: Ocorreu um erro inesperado em um envio.” Com certificado SSL

119

Problema: recebo esta exceção "A CONEXÃO SUBJACENTE FOI FECHADA: UM ERRO INESPERADO OCORREU EM UM ENVIO" em meus registros e está interrompendo nossa integração de OEM com nosso sistema de e-mail marketing em horários aleatórios variando de [1 hora - 4 horas]

Meu site está hospedado em um servidor Windows 2008 R2 com IIS 7.5.7600. Este site possui um grande número de componentes OEM e um painel abrangente. Tudo funciona bem com todos os outros elementos do site, exceto com um de nossos componentes de marketing por e-mail, que estamos usando como uma solução iframe em nosso painel. Funciona assim: envio um objeto httpWebRequest com todas as credenciais e recebo de volta um url que coloquei em um iframe e funciona. Mas isso só funciona por algum tempo [1 hora - 4 horas] e então eu obtenho a exceção abaixo "A CONEXÃO SUBJACENTE FOI FECHADA: UM ERRO INESPERADO OCORREU EM UM ENVIO" e mesmo se o sistema tentar obter o URL do httpWebRequest falha com a mesma exceção. A única maneira de fazê-lo funcionar novamente é reciclar o pool de aplicativos ou qualquer coisa é editada em web.config.

Opção tentada

Adicionado explicitamente, keep-alive = false

keep-alive = true

Aumento do tempo limite: <httpRuntime maxRequestLength="2097151" executionTimeout="9999999" enable="true" requestValidationMode="2.0" />

Eu carreguei esta página para um site não SSL para verificar se o certificado SSL em nosso servidor de produção está fazendo a conexão para cair de alguma forma.

Qualquer direção para a resolução é muito 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`
Arvind Morwal
fonte
Você já descobriu isso?
Brett G
11
sim, consegui fazê-lo funcionar com este código ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls Ou SecurityProtocolType.Ssl3
Arvind Morwal 02 de
Eu estava prestes a morrer pelo mesmo problema. Levei várias horas lutando com o mesmo problema. Obrigado por seus comentários, isso salvou meu dia.
Sameers Javed
2
@ user3458212 você deve adicionar seu comentário como uma resposta
icc97
2
No meu caso, rodar o site no Visual Studio 15 está tudo bem, mas no final, como não consigo atualizar a estrutura no servidor e forçar o TLS 1.2 e desativar o keep-alive não funcionou, tive que configurar um intermediário servidor da web para proxy do servidor da web de destino que interrompe a conexão.
José Roberto García Chico

Respostas:

193

Para mim foi tls12:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
Lanklaas
fonte
42
Observe que você deve ter cuidado porque essa alteração é global para o AppDomain e fará com que as chamadas para qualquer site que não ofereça TLS 1.2 falhem (o que você pode preferir se os dados a serem transportados forem realmente confidenciais). Para preferir TLS 1.2, mas ainda permitir 1.1 e 1.0, você tem que fazer OR:ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
Dusty
mesmo aqui, você salvou minha vida, gastou tanto tempo tentando descobrir o que havia de errado com o RestSharp
darul75
Esta solução também funciona para aqueles que não usam RestSharp e também para aqueles que não usam ServicePointManager. Basta copiar e colar a linha acima antes de sua chamada WebRequest ou o que você estiver usando para fazer a solicitação. Eu inicialmente ignorei essa solução por causa dos motivos acima.
goku_da_master
ou apenas adicione ao que já está lá ... System.Net.ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
uosjead
8
Para fazer isso no PowerShell, "binário ou" eles juntos assim:[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls
Adam S
62

Se você estiver preso com .Net 4.0 e o site de destino estiver usando TLS 1.2, você precisará da seguinte linha. ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;

fonte: TLS 1.2 e suporte .NET: como evitar erros de conexão

Tochi
fonte
5
Impressionante! Gostaria apenas de acrescentar que (SecurityProtocolType)768pode ser usado para "Tls11" (ou seja, TLS 1.1).
Solomon Rutzky
2
Isso realmente ajuda. Isso salvou meu dia. Eu tenho que ficar com .Net 2.0.
Hao Nguyen
22

O código abaixo resolveu o problema

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls Or SecurityProtocolType.Ssl3
Arvind Morwal
fonte
5
Isso funcionaria, no entanto, lembre-se de que ServicePointManager.SecurityProtocolé um objeto estático, o que significa que a alteração desse valor afetará todas as subseqüências WebRequestou WebClientchamadas. Você pode criar separado AppDomainse quiser ServicePointManagerter configurações diferentes. Consulte stackoverflow.com/questions/3791629/… para obter mais detalhes.
stack247 de
1
Leitura adicional útil para entender o que este código está fazendo: stackoverflow.com/questions/26389899/…
Jon Schneider
@Marnee eu coloquei na raiz de composição do meu aplicativo, para que seja definido antes de qualquer I / O que ocorra
JG em SD de
@Marnee Coloque-o em um construtor estático, para que seja executado exatamente uma vez, na primeira vez que a classe é acessada. Tive que habilitar todos os protocolos, para cobrir todos os casos.
Nyerguds 01 de
14

Estou tendo o mesmo problema há dias com uma integração que também "costumava funcionar antes".

Por pura depressão, eu apenas tentei

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

Isso resolveu para mim ... embora a integração estritamente apenas faça uso de SSLv3.

Cheguei à conclusão de que algo estava errado desde que o Fiddler relatou que há uma "cifra de negociação TLS vazia" ou algo parecido.

Espero que funcione!

Medismal
fonte
Observe que, mesmo que seu código não precise de TLS, se o servidor com o qual está se comunicando, ele tentará negociar com TLS e falhará se não puder. A cifra de negociação TLS vazia era o slot que esperava a troca do protocolo TLS que você acabou de fornecer. Provavelmente "costumava funcionar antes" porque o administrador do servidor provavelmente acabou de habilitar o TLS no servidor com o qual seu aplicativo estava se comunicando.
vapcguy
13

No meu caso, o site ao qual estou me conectando foi atualizado para o TLS 1.2. Como resultado, tive que instalar o .net 4.5.2 no meu servidor web para suportá-lo.

aboy021
fonte
Isso pode ser feito no nível do registro na máquina? Desativei todos os protocolos SSL, deixando TLS 1.0, 1.1, 1.2, no entanto, é meu entendimento que qualquer coisa menor que TLS 1.2 deve ser removido em breve para ser compatível com PCI.
brendo234
12

Acesse seu web.config / App.config para verificar qual .net runtime você está usando

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

Aqui está a solução:

  1. .NET 4.6 e superior. Você não precisa fazer nenhum trabalho adicional para oferecer suporte a TLS 1.2, ele é compatível por padrão.

  2. .NET 4.5. TLS 1.2 é compatível, mas não é um protocolo padrão. Você precisa optar por usá-lo. O código a seguir tornará o TLS 1.2 padrão, certifique-se de executá-lo antes de fazer uma conexão com o recurso seguro:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12

  1. .NET 4.0. O TLS 1.2 não é compatível, mas se você tiver o .NET 4.5 (ou superior) instalado no sistema, ainda poderá optar pelo TLS 1.2, mesmo que a estrutura do seu aplicativo não seja compatível. O único problema é que SecurityProtocolType no .NET 4.0 não tem uma entrada para TLS1.2, então teríamos que usar uma representação numérica desse valor enum:

ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;

  1. .NET 3.5 ou inferior. O TLS 1.2 não é compatível (*) e não há solução alternativa. Atualize seu aplicativo para uma versão mais recente da estrutura.
Guo Huang
fonte
6

Descobri que isso é um sinal de que o servidor onde você está implantando o código tem um antigo .NET framework instalado que não oferece suporte a TLS 1.1 ou TLS 1.2. Passos para corrigir:

  1. Instalando o .NET Runtime mais recente em seus servidores de produção (IIS e SQL)
  2. Instalando o .NET Developer Pack mais recente em suas máquinas de desenvolvimento.
  3. Altere as configurações de "Estrutura de destino" em seus projetos do Visual Studio para a estrutura .NET mais recente.

Você pode obter o .NET Developer Pack e o Runtime mais recentes neste URL: http://getdotnet.azurewebsites.net/target-dotnet-platforms.html

Patrick
fonte
Alterei a estrutura de destino de 4.5.2 para 4.6.1 e comecei a trabalhar, obrigado Patrick.
Vivek Sharma de
4

Tivemos esse problema em que um site que estava acessando nossa API recebia a mensagem “A conexão subjacente foi fechada: Ocorreu um erro inesperado em um envio”. mensagem.

O código deles era uma mistura de .NET 3.xe 2.2, o que, pelo que entendi, significa que eles estão usando TLS 1.0.

A resposta abaixo pode ajudá-lo a diagnosticar o problema ativando TLS 1.0, SSL 2 e SSL3, mas para ser muito claro, você não quer fazer isso a longo prazo, pois todos os três protocolos são considerados inseguros e não deveriam mais ser usado :

Para fazer o nosso IIS responder às suas chamadas de API, tivemos que adicionar configurações de registro no servidor do IIS para habilitar explicitamente as versões do TLS - NOTA: Você deve reiniciar o servidor Windows (não apenas o serviço IIS) depois de fazer essas alterações:

[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

Se isso não funcionar, você também pode experimentar adicionar a 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 claro, essa não é uma boa solução , e a solução certa é fazer o chamador usar o TLS 1.2, mas o que foi dito acima pode ajudar a diagnosticar que esse é o problema.

Você pode adicionar rapidamente essas entradas de registro com este script do 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

Essa é uma versão modificada do script da página de ajuda da Microsoft para Configurar TLS para VMM . Este artigo do basics.net foi a página que originalmente me deu a ideia de examinar essas configurações.

tomRedox
fonte
Nosso problema era em torno de um pipeline de lançamento via Team City que parou repentinamente quando alteramos o certificado para seu servidor ativo. Já tínhamos mudado o servidor para usar apenas TLS1.2 e nosso pipeline Team City parou de funcionar ... Funcionou como um sonho ... adicionamos as entradas de registro e reiniciou o servidor ... BOOM !!! Obrigado tomRedox !!
Gwasshoppa
@Gwasshoppa, apenas para reiterar que o acima exposto é apenas um paliativo para diagnosticar o problema. Agora que você sabe que é um problema de versão do TLS, a solução é alterar o pipeline de lançamento para que possa funcionar com o TLS1.2 e, em seguida, desligar o TLS <1.2 e o SSL 2 e 3 novamente. Eu atualizei a resposta acima um pouco para enfatizar isso também.
tomRedox
4

Basta adicionar:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

M.Qudah
fonte
1
Forneça uma explicação com sua resposta.
Word Rearranger
4
Esta resposta também não acrescenta nada às anteriores.
Arvindh Mani
2

Se ajudar alguém, nosso problema foi com falta de certificado. O ambiente é Windows Server 2016 Standard com .Net 4.6.

Há um URI https de serviço WCF auto-hospedado, para o qual Service.Open () seria executado sem erros. Outro encadeamento continuaria acessando https: // OurIp: 443 / OurService? Wsdl para garantir que o serviço estivesse disponível. Acessar o WSDL costumava falhar com:

A conexão subjacente foi fechada: Ocorreu um erro inesperado em um envio.

Usar ServicePointManager.SecurityProtocol com configurações aplicáveis ​​não funcionou. Jogar com funções e recursos do servidor também não ajudou. Em seguida, interveio Jaise George , o SE, resolvendo o problema em alguns minutos. Jaise instalou um certificado autoassinado no IIS, resolvendo o problema. Isso é o que ele fez para resolver o problema:

(1) Abra o gerenciador IIS (inetmgr) (2) Clique no nó do servidor no painel esquerdo e clique duas vezes em "Certificados do servidor". (3) Clique em "Criar certificado autoassinado" no painel direito e digite o que quiser para o nome amigável. (4) Clique em “Site padrão” no painel esquerdo, clique em "Ligações" no painel direito, clique em "Adicionar", selecione "https", selecione o certificado que você acabou de criar e clique em "OK" (5) Acesso o URL https, deve estar acessível.

DiligentKarma
fonte
Você pensaria que o administrador do servidor teria adicionado o certificado SSL! D'oh! lol :) Posso ver isso acontecendo, no entanto - ou um certificado expirado que precisa de renovação provavelmente seria o cenário mais aplicável.
vapcguy
2

Basta alterar a versão do aplicativo, como 4.0 para 4.6, e publicar esse código.

Adicione também as linhas de código abaixo:

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

Usar um proxy de depuração HTTP pode causar isso - como o Fiddler.

Eu estava carregando um certificado PFX de um arquivo local (autenticação para Apple.com) e ele falhou porque o Fiddler não foi capaz de passar este certificado adiante.

Tente desativar o Fiddler para verificar e, se essa for a solução, você provavelmente precisará instalar o certificado em sua máquina ou de alguma forma que o Fiddler possa usá-lo.

Simon_Weaver
fonte
0

O código abaixo resolveu meu problema:

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