"La connexion sous-jacente a été fermée: une erreur inattendue s'est produite lors d'un envoi." Avec certificat SSL

120

Problème: j'obtiens cette exception "LA CONNEXION SOUS-JACENTE A ÉTÉ FERMÉE: UNE ERREUR INATTENDUE SURVENUE SUR UN ENVOI" dans mes journaux et cela rompt notre intégration OEM avec notre système de marketing par e-mail à des moments aléatoires variant de [1heure à 4 heures]

Mon site Web est hébergé sur un serveur Windows 2008 R2 avec IIS 7.5.7600. Ce site Web contient un grand nombre de composants OEM et un tableau de bord complet. Tout fonctionne bien avec tous les autres éléments du site Web, à l'exception de l'un de nos composants de marketing par e-mail que nous utilisons comme solution iframe dans notre tableau de bord. La façon dont cela fonctionne est, j'envoie un httpWebRequestobject avec toutes les informations d'identification et je récupère une URL que j'ai mise dans un iframe et cela fonctionne. Mais cela ne fonctionne que pendant un certain temps [1 heure - 4 heures], puis j'obtiens l'exception ci-dessous "LA CONNEXION SOUS-JACENTE A ÉTÉ FERMEE: UNE ERREUR INATTENDUE SURVENUE SUR UN ENVOI" et même si le système essaie d'obtenir l'URL du httpWebRequest it échoue avec la même exception. La seule façon de le faire fonctionner à nouveau est de recycler le pool d'applications ou quoi que ce soit est modifié dans web.config.

Option essayée

Explicitement ajouté, keep-alive = false

keep-alive = true

Augmentation du temps mort: <httpRuntime maxRequestLength="2097151" executionTimeout="9999999" enable="true" requestValidationMode="2.0" />

J'ai téléchargé cette page sur un site Web non SSL pour vérifier si le certificat SSL sur notre serveur de production établit la connexion pour supprimer une certaine façon.

Toute direction vers la résolution est grandement appréciée.

Code:

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
la source
Avez-vous déjà compris cela?
Brett G
12
oui, j'ai pu le faire fonctionner avec ce code ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls Ou SecurityProtocolType.Ssl3
Arvind Morwal
J'étais sur le point de mourir pour le même problème. Cela m'a pris plusieurs heures à lutter avec le même problème. Merci pour vos commentaires, cela m'a sauvé la journée.
Sameers Javed
2
@ user3458212, vous devriez ajouter votre commentaire en tant que réponse
icc97
2
Dans mon cas, exécuter le site Web dans Visual Studio 15 tout se passe bien, mais à la fin, comme je ne peux pas mettre à niveau le framework dans le serveur, et forcer TLS 1.2 et désactiver Keep-Alive ne fonctionne pas, j'ai dû configurer un intermédiaire serveur Web pour proxy le serveur Web cible qui interrompt la connexion.
José Roberto García Chico

Réponses:

194

Pour moi, c'était tls12:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
Lanklaas
la source
42
Notez que vous devez être prudent car ce changement est global à votre AppDomain et entraînera l'échec des appels à tout site qui n'offre pas TLS 1.2 (ce que vous préférerez peut-être si les données à transporter sont vraiment sensibles). Pour préférer TLS 1.2 tout en autorisant 1.1 et 1.0, vous devez les OU:ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
Dusty
même ici, vous m'avez sauvé la vie, passé tellement de temps à comprendre ce qui n'allait pas avec RestSharp
darul75
Cette solution fonctionne également pour ceux qui n'utilisent pas RestSharp ainsi que pour ceux qui n'utilisent pas ServicePointManager. Copiez et collez simplement la ligne ci-dessus avant votre appel WebRequest ou tout ce que vous utilisez pour faire la demande. J'ai initialement ignoré cette solution pour les raisons ci-dessus.
goku_da_master
ou ajoutez-le simplement à ce qui existe déjà ... System.Net.ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
uosjead
8
Pour faire cela dans PowerShell, "binaire ou" les ensemble comme ceci:[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls
Adam S
64

Si vous êtes bloqué avec .Net 4.0 et que le site cible utilise TLS 1.2, vous avez besoin de la ligne suivante à la place. ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;

source: Prise en charge de TLS 1.2 et .NET: comment éviter les erreurs de connexion

Tochi
la source
5
Impressionnant! J'ajouterais simplement qui (SecurityProtocolType)768peut être utilisé pour "Tls11" (ie TLS 1.1).
Solomon Rutzky
2
Cela aide vraiment. Cela m'a sauvé la journée. Je dois m'en tenir à .Net 2.0.
Hao Nguyen
24

Le code ci-dessous a résolu le problème

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls Or SecurityProtocolType.Ssl3
Arvind Morwal
la source
6
Cela fonctionnerait, cependant, gardez à l'esprit qu'il ServicePointManager.SecurityProtocols'agit d'un objet statique, ce qui signifie que la modification de cette valeur affectera toutes les sous-séquences WebRequestou tous les WebClientappels. Vous pouvez créer des fichiers séparés AppDomainsi vous souhaitez ServicePointManageravoir des paramètres différents. Voir stackoverflow.com/questions/3791629/… pour plus de détails.
stack247
1
Lectures supplémentaires utiles pour comprendre ce que fait ce code: stackoverflow.com/questions/26389899/…
Jon Schneider
@Marnee Je l'ai mis dans la racine de composition de mon application, il est donc défini avant toute E / S
JG in SD
@Marnee Mettez-le dans un constructeur statique, donc il est exécuté exactement une fois, la première fois que la classe est accédée. Cependant, j'ai dû activer tous les protocoles, pour couvrir tous les cas.
Nyerguds le
14

Cela fait des jours que j'ai le même problème avec une intégration qui "fonctionnait auparavant".

Par pure dépression, j'ai juste essayé

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

Cela a résolu le problème pour moi ... même si l'intégration n'utilise strictement que SSLv3.

Je suis venu à la réalisation que quelque chose ne fonctionnait pas depuis que Fiddler avait rapporté qu'il y avait un "chiffrement de négociation TLS vide" ou quelque chose du genre.

Espérons que cela fonctionne!

Médisme
la source
Notez que, même si votre code n'a pas besoin de TLS, si le serveur avec lequel il communique DOES, il essaiera de négocier avec TLS et échouera s'il ne le peut pas. Le chiffrement de négociation TLS vide était le slot qui attendait l'échange de protocole TLS que vous avez finalement fourni. Cela "fonctionnait avant" parce que l'administrateur du serveur venait probablement d'activer TLS sur le serveur avec lequel votre application communiquait.
vapcguy le
13

Dans mon cas, le site auquel je me connecte a été mis à niveau vers TLS 1.2. En conséquence, j'ai dû installer .net 4.5.2 sur mon serveur Web pour le prendre en charge.

aboy021
la source
Cela peut-il être fait au niveau du registre sur la machine? J'ai désactivé tous les protocoles SSL, laissant TLS 1.0, 1.1, 1.2, mais je crois comprendre que tout ce qui est inférieur à TLS 1.2 devrait être bientôt supprimé pour être conforme à PCI.
brendo234
13

Accédez à votre web.config / App.config pour vérifier le runtime .net que vous utilisez

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

Voici la solution:

  1. .NET 4.6 et supérieur. Vous n'avez pas besoin de faire de travail supplémentaire pour prendre en charge TLS 1.2, il est pris en charge par défaut.

  2. .NET 4.5. TLS 1.2 est pris en charge, mais ce n'est pas un protocole par défaut. Vous devez vous inscrire pour l'utiliser. Le code suivant définira TLS 1.2 par défaut, assurez-vous de l'exécuter avant de vous connecter à une ressource sécurisée:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12

  1. .NET 4.0. TLS 1.2 n'est pas pris en charge, mais si .NET 4.5 (ou supérieur) est installé sur le système, vous pouvez toujours opter pour TLS 1.2 même si votre framework d'application ne le prend pas en charge. Le seul problème est que SecurityProtocolType dans .NET 4.0 n'a pas d'entrée pour TLS1.2, nous devrions donc utiliser une représentation numérique de cette valeur d'énumération:

ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;

  1. .NET 3.5 ou inférieur. TLS 1.2 n'est pas pris en charge (*) et il n'y a pas de solution de contournement. Mettez à niveau votre application vers une version plus récente du framework.
Guo Huang
la source
6

J'ai trouvé que c'est un signe que le serveur sur lequel vous déployez du code a un ancien framework .NET installé qui ne prend pas en charge TLS 1.1 ou TLS 1.2. Étapes à suivre:

  1. Installation du dernier Runtime .NET sur vos serveurs de production (IIS et SQL)
  2. Installation du dernier pack développeur .NET sur vos machines de développement.
  3. Modifiez les paramètres «Framework cible» de vos projets Visual Studio vers le dernier framework .NET.

Vous pouvez obtenir le dernier pack de développement .NET et Runtime à partir de cette URL: http://getdotnet.azurewebsites.net/target-dotnet-platforms.html

Patrick
la source
Changement du cadre cible de 4.5.2 à 4.6.1, et a commencé à travailler, merci Patrick.
Vivek Sharma
4

Nous avons rencontré ce problème en raison duquel un site Web qui accédait à notre API obtenait le message "La connexion sous-jacente a été fermée: une erreur inattendue s'est produite lors d'un envoi." message.

Leur code était un mélange de .NET 3.x et 2.2, ce qui, d'après ce que je comprends, signifie qu'ils utilisent TLS 1.0.

La réponse ci-dessous peut vous aider à diagnostiquer le problème en activant TLS 1.0, SSL 2 et SSL3, mais pour être très clair, vous ne voulez pas faire cela à long terme car ces trois protocoles sont considérés comme non sécurisés et ne devraient plus l'être utilisé :

Pour que notre IIS réponde à leurs appels d'API, nous avons dû ajouter des paramètres de registre sur le serveur IIS pour activer explicitement les versions de TLS - REMARQUE: vous devez redémarrer le serveur Windows (pas seulement le service IIS) après avoir effectué ces modifications:

[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 cela ne le fait pas, vous pouvez également essayer d'ajouter l'entrée pour 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

Pour être clair, ce n'est pas une bonne solution , et la bonne solution consiste à amener l'appelant à utiliser TLS 1.2, mais ce qui précède peut aider à diagnostiquer que c'est le problème.

Vous pouvez accélérer l'ajout de ces entrées de registre avec ce script 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

Il s'agit d'une version modifiée du script de la page d'aide Microsoft pour Configurer TLS pour VMM . Cet article de basics.net est la page qui m'a initialement donné l'idée d'examiner ces paramètres.

tomRedox
la source
Notre problème concernait un pipeline de versions via Team City qui s'est soudainement arrêté lorsque nous avons changé le certificat pour votre serveur en direct. Nous avions déjà changé le serveur pour n'utiliser que TLS1.2 et notre pipeline Team City a cessé de fonctionner ... A fonctionné comme un rêve ... ajouté les entrées de registre et redémarré le serveur ... BOOM !!! Merci tomRedox !!
Gwasshoppa
@Gwasshoppa, juste pour réitérer que ce qui précède n'est qu'un palliatif pour diagnostiquer le problème. Maintenant que vous savez qu'il s'agit d'un problème de version TLS, la solution consiste à modifier le pipeline de versions afin qu'il puisse fonctionner avec TLS1.2, puis à désactiver à nouveau TLS <1.2 et SSL 2 et 3. J'ai légèrement mis à jour la réponse ci-dessus pour le souligner également.
tomRedox
4

Il suffit d'ajouter:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

M.Qudah
la source
1
Veuillez fournir une explication avec votre réponse.
Word Rearranger
4
Cette réponse n'ajoute rien aux précédentes non plus.
Arvindh Mani
2

Si cela aide quelqu'un, le nôtre était un problème de certificat manquant. L'environnement est Windows Server 2016 Standard avec .Net 4.6.

Il existe un URI https de service WCF auto-hébergé, pour lequel Service.Open () s'exécuterait sans erreur. Un autre thread continuerait d'accéder à https: // OurIp: 443 / OurService? Wsdl pour s'assurer que le service était disponible. L'accès au WSDL échouait avec:

La connexion sous-jacente a été fermée: une erreur inattendue s'est produite lors d'un envoi.

L'utilisation de ServicePointManager.SecurityProtocol avec les paramètres applicables ne fonctionnait pas. Jouer avec les rôles et les fonctionnalités du serveur n'a pas non plus aidé. Puis est intervenu Jaise George , le SE, résolvant le problème en quelques minutes. Jaise a installé un certificat auto-signé dans l'IIS, ce qui a résolu le problème. Voici ce qu'il a fait pour résoudre le problème:

(1) Ouvrez le gestionnaire IIS (inetmgr) (2) Cliquez sur le nœud du serveur dans le panneau de gauche, et double-cliquez sur "Certificats de serveur". (3) Cliquez sur "Créer un certificat auto-signé" dans le panneau de droite et tapez tout ce que vous voulez pour le nom convivial. (4) Cliquez sur «Site Web par défaut» dans le panneau de gauche, cliquez sur «Liaisons» dans le panneau de droite, cliquez sur «Ajouter», sélectionnez «https», sélectionnez le certificat que vous venez de créer et cliquez sur «OK» (5) Accéder l'URL https, elle doit être accessible.

DiligentKarma
la source
Vous auriez pensé que l'administrateur du serveur aurait ajouté le certificat SSL, cependant! Oh! lol :) Je peux voir cela se produire, cependant - ou un certificat expiré qui doit être renouvelé serait probablement le scénario le plus applicable.
vapcguy le
2

Il vous suffit de changer la version de votre application comme 4.0 en 4.6 et de publier ce code.

Ajoutez également ci-dessous les lignes de code:

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

L'utilisation d'un proxy de débogage HTTP peut provoquer ce problème, tel que Fiddler.

Je chargeais un certificat PFX à partir d'un fichier local (authentification sur Apple.com) et cela a échoué car Fiddler n'a pas pu transmettre ce certificat.

Essayez de désactiver Fiddler pour vérifier et si c'est la solution, vous devez probablement installer le certificat sur votre machine ou d'une manière que Fiddler puisse l'utiliser.

Simon_Weaver
la source
0

Le code ci-dessous a résolu mon problème:

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