"The underlying connection was closed: An unexpected error occurred on a send." With SSL Certificate
Asked Answered
N

16

154

Issue

I get this exception

The underlying connection was closed: An unexpected error occurred on a send.

in my logs, and it is breaking our OEM integration with our e-mail marketing system at random times. (varying from 1-4 hours)

My website is hosted on a Windows Server 2008 R2 with IIS 7.5.7600.

This website has a large number of OEM components, and comprehensive dashboard. Everything works fine with all the other elements of the website except with one of our e-mail marketing component which we are using as an iframe solution within our dashboard.

The way it works is, I send a HttpWebRequest object with all the credentials, and I get a url back which I put in an iframe and it works.

But it only works for some time (1-4 hours), and then from the call to

webRequest.GetResponse();

I get the exception

The underlying connection was closed: An unexpected error occurred on a send.

Even if the system tries to get the URL from the httpWebRequest it fails with the same exception.

The only way to make it work again is:

  • to recycle the application pool
  • anything is edited in web.config.

I am really exhausted all the option that i could think of.

Options tried

Explicitly added,

  • keep-alive = false

  • keep-alive = true

  • Increased the time out:

    <httpRuntime maxRequestLength="2097151" executionTimeout="9999999" enable="true" requestValidationMode="2.0" />

I have uploaded this page to a non SSL website to check if the SSL certificate on our production server is making the connection to drop some how.

Any direction toward resolution is greatly appreciated.

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
Nate answered 25/3, 2014 at 7:27 Comment(3)
yes I was able to get it working with this code ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls Or SecurityProtocolType.Ssl3Nate
@user3458212 you should add your comment in as an answerIntrastate
In my case, running the web site in Visual Studio 15 everything goes fine, but at the end, because I cannot upgrade framework in the server, and forcing TLS 1.2 and disabling keep-alive don't work, I had to setup an intermediate web server to proxy the target web server that drops the connection.Boschvark
S
252

For me it was tls12:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
Sattler answered 8/1, 2016 at 13:48 Comment(6)
Note that you must be careful because this change is global to your AppDomain, and will cause calls to any site which doesn't offer TLS 1.2 to fail (which you may prefer if the data to be transported is truly sensitive). To prefer TLS 1.2 but still allow 1.1 and 1.0, you have to OR them: ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;Koodoo
same here, you saved my life, spent so much time to figure out what was wrong with RestSharpAsymptotic
This solution also works for those not using RestSharp as well as for those that are not using ServicePointManager. Just copy and paste the above line before your WebRequest call or whatever you're using to make the request. I initially ignored this solution because of the above reasons.Indulge
or just add it to what's already there... System.Net.ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;Aceto
To do this in PowerShell, "binary or" them together like so: [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::TlsSerology
For VB.Net, use Xor: ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 Xor SecurityProtocolType.Tls11 Xor SecurityProtocolType.TlsMagnifico
E
86

If you are stuck with .Net 4.0 and the target site is using TLS 1.2, you need the following line instead. ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;

source: TLS 1.2 and .NET Support: How to Avoid Connection Errors

Eckart answered 27/10, 2016 at 23:27 Comment(1)
Awesome! I would just add that (SecurityProtocolType)768 can be used for "Tls11" (i.e. TLS 1.1).Jiminez
H
31

Go to your web.config/App.config to verify which .net runtime you are using

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

Here is the solution:

  1. .NET 4.6 and above. You don’t need to do any additional work to support TLS 1.2, it’s supported by default.

  2. .NET 4.5. TLS 1.2 is supported, but it’s not a default protocol. You need to opt-in to use it. The following code will make TLS 1.2 default, make sure to execute it before making a connection to secured resource:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12

  1. .NET 4.0. TLS 1.2 is not supported, but if you have .NET 4.5 (or above) installed on the system then you still can opt in for TLS 1.2 even if your application framework doesn’t support it. The only problem is that SecurityProtocolType in .NET 4.0 doesn’t have an entry for TLS1.2, so we’d have to use a numerical representation of this enum value:

ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;

  1. .NET 3.5 or below. TLS 1.2 is not supported (*) and there is no workaround. Upgrade your application to more recent version of the framework.
Hypersthene answered 6/8, 2018 at 21:1 Comment(1)
+1 - this part of your answer is key make sure to execute it before making a connection to secured resource:. I had set the SecurityProtocol after creating my request with var request = (HttpWebRequest)WebRequest.Create(url);. This meant that the first request always failed, but subsequent requests were fine. Moving the set to before creating the request object sorted it.Reconcile
N
28

The code below resolved the issue

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls Or SecurityProtocolType.Ssl3
Nate answered 13/7, 2015 at 22:13 Comment(6)
This would work, however, keep in mind that ServicePointManager.SecurityProtocol is a static object which means changing this value will affect all sub-sequence WebRequest or WebClient calls. You could create separate AppDomain if you want ServicePointManager to have different settings. See #3792129 for more details.Naji
Helpful additional reading to understand what this code is doing: #26390399Fulmination
@Marnee I put it in my application's composition root, so it is set prior to any I/O ever occuringInclement
@Marnee Put it in a static constructor, so it's executed exactly once, the first time the class is accessed. I had to enable all protocols though, to cover all cases.Thetisa
Set this value BEFORE WebRequest object creation. If the object has been already created with the "old" ServicePointManager settings, it will use those, even if you have set new ones just before issuing a request to a server.Anabantid
SecurityProtocolType.Ssl3 was compromised in 2014 with the POODLE attack. Do NOT use SSLv3 en.wikipedia.org/wiki/POODLEHamil
H
16

I've been having the same issue for days now with an integration that also just "used to work before".

Out of sheer depression, I just tried

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

This solved it for me..even though the integration strictly only makes use of SSLv3.

I came to the realization that something if off since Fiddler reported saying that there is an "empty TLS negotiation cipher" or something of the like.

Hopefully it works!

Hemicellulose answered 9/6, 2016 at 7:31 Comment(2)
Note that, even if your code doesn't need TLS, if the server it is communicating with DOES, it will try to negotiate with TLS and fail if it can't. The empty TLS negotiation cipher was the slot that was expecting the TLS protocol exchange you eventually supplied. It likely "used to work before" because the server admin likely had just enabled TLS on the server your application was communicating with.Amati
SecurityProtocolType.Ssl3 was compromised in 2014 with the POODLE attack. Do NOT use SSLv3 en.wikipedia.org/wiki/POODLEHamil
T
15

In my case the site that I'm connecting to has upgraded to TLS 1.2. As a result I had to install .net 4.5.2 on my web server in order to support it.

Tendentious answered 25/5, 2015 at 4:38 Comment(1)
Can this be done at the registry level on the machine? I've disabled all SSL protocols, leaving TLS 1.0, 1.1, 1.2, however it is my understanding that anything less than TLS 1.2 should be removed soon in order to be PCI compliant.Alysaalyse
M
7

Just add:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

Mathematics answered 13/3, 2019 at 15:11 Comment(2)
Please provide an explanation with your answer.Kroo
This answer adds nothing to previous ones either.Await
F
6

I've found that this is a sign that the server where you're deploying code has an old .NET framework installed that doesn't support TLS 1.1 or TLS 1.2. Steps to fix:

  1. Installing the latest .NET Runtime on your production servers (IIS & SQL)
  2. Installing the latest .NET Developer Pack on your development machines.
  3. Change the "Target framework" settings in your Visual Studio projects to the latest .NET framework.

You can get the latest .NET Developer Pack and Runtime from this URL: http://getdotnet.azurewebsites.net/target-dotnet-platforms.html

Freedom answered 7/9, 2016 at 13:16 Comment(1)
Changed target framework from 4.5.2 to 4.6.1, and started working, thank you Patrick.Amir
M
6

I found Solution by updating web.config with below code.

My .Net Framwork was set to 4.0 and after organization level TLS protocol update I suddenly started facing this issue. So I referred above answer of "Guo Huang" and updated the targetFramework and it worked.

<system.web>
        <compilation debug="true" targetFramework="4.7.2" />
        <httpRuntime targetFramework="4.7.2"  maxRequestLength="102400" executionTimeout="3600"/>
</system.web>
Murrain answered 3/5, 2021 at 11:33 Comment(1)
In my case, the Exception was occurring randomly for the same code. I checked many solutions, but only this one helped - the part with executionTimeout="3600". Thanks!Roadside
L
5

We had this issue whereby a website that was accessing our API was getting the “The underlying connection was closed: An unexpected error occurred on a send.” message.

Their code was a mix of .NET 3.x and 2.2, which as I understand it means they are using TLS 1.0.

The answer below can help you diagnose the issue by enabling TLS 1.0, SSL 2 and SSL3, but to be very clear, you do not want to do that long-term as all three of those protocols are regarded as insecure and should no longer be used:

To get our IIS to respond to their API calls we had to add registry settings on the IIS's server to explicitly enable versions of TLS - NOTE: You have to restart the Windows server (not just the IIS service) after making these changes:

[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

If that doesn't do it, you could also experiment with adding the entry for 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

To be clear, this is not a nice solution, and the right solution is to get the caller to use TLS 1.2, but the above can help diagnose that this is the issue.

You can speed adding those reg entries up with this powershell script:

$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

That's a modified version of the script from the Microsoft help page for Set up TLS for VMM. This basics.net article was the page that originally gave me the idea to look at these settings.

Loreleilorelie answered 24/8, 2018 at 12:50 Comment(3)
Our issue was around a release pipeline via Team City that suddenly stopped when we changed the certificate for you live server. We had already changed the server to only use TLS1.2 and our Team City pipeline stopped working... Worked like a dream... added the reg entries and restarted the server... BOOM!!! Thanks tomRedox!!Rightist
@Gwasshoppa, just to reiterate that the above is only a stopgap to diagnose the issue. Now you know it's a TLS version issue the solution is to change the release pipeline so that it can work with TLS1.2 and then switch off TLS <1.2 and SSL 2 and 3 again. I've updated the answer above slightly to stress that too.Loreleilorelie
"SSL 3.0" was compromised in 2014 with the POODLE attack. Do NOT use SSLv3 en.wikipedia.org/wiki/POODLEHamil
P
3

It if helps someone, ours was an issue with missing certificate. Environment is Windows Server 2016 Standard with .Net 4.6.

There is a self hosted WCF service https URI, for which Service.Open() would execute without errors. Another thread would keep accessing https://OurIp:443/OurService?wsdl to ensure that the service was available. Accessing the WSDL used to fail with:

The underlying connection was closed: An unexpected error occurred on a send.

Using ServicePointManager.SecurityProtocol with applicable settings did not work. Playing with server roles and features did not help either. Then stepped in Jaise George, the SE, resolving the issue in a couple of minutes. Jaise installed a self signed certificate in the IIS, poofing the issue. This is what he did to address the issue:

(1) Open IIS manager (inetmgr) (2) Click on the server node in the left panel, and double click "Server certificates". (3) Click on "Create Self-Signed Certificate" on the right panel and type in anything you want for the friendly name. (4) Click on “Default Web site” in the left panel, click "Bindings" on the right panel, click "Add", select "https", select the certificate you just created, and click "OK" (5) Access the https URL, it should be accessible.

Psycholinguistics answered 3/12, 2018 at 23:2 Comment(1)
You would've thought the server admin would've added the SSL cert, though! D'oh! lol :) I can see this happening, though - or an expired cert that needs renewing would likely be the more applicable scenario.Amati
C
2

You just change your application version like 4.0 to 4.6 and publish those code.

Also add below code lines:

httpRequest.ProtocolVersion = HttpVersion.Version10; 
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
Chercherbourg answered 9/7, 2019 at 8:45 Comment(1)
SecurityProtocolType.Ssl3 was compromised in 2014 with the POODLE attack. Do NOT use SSLv3 en.wikipedia.org/wiki/POODLEHamil
F
1

Using a HTTP debugging proxy can cause this - such as Fiddler.

I was loading a PFX certificate from a local file (authentication to Apple.com) and it failed because Fiddler wasn't able to pass this certificate on.

Try disabling Fiddler to check and if that is the solution then you need to probably install the certificate on your machine or in some way that Fiddler can use it.

Fainthearted answered 27/11, 2017 at 22:42 Comment(0)
K
0

The below code solved my problem :

request.ProtocolVersion = HttpVersion.Version10; // THIS DOES THE TRICK
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
Kokand answered 6/7, 2018 at 6:2 Comment(1)
SecurityProtocolType.Ssl3 was compromised in 2014 with the POODLE attack. Do NOT use SSLv3 en.wikipedia.org/wiki/POODLEHamil
A
0

It's either no shared TLS setting &/or no shared cipher between your app-domain (the client) and the site (the server). Each app-domain (e.g. powershell, cmd, your app etc.) picks up the host's default client settings (or overrides them), so you could have e.g. cmd shell working, but powershell failing etc.

You can check the target url using e.g. https://www.ssllabs.com/ssltest/analyze.html?d=api.nuget.org

and you can check your app-domains setting using e.g.

[Net.ServicePointManager]::SecurityProtocol
and
Get-TlsCipherSuite | Format-Table Name

Weakening your TLS settings to e.g. allow ssl3, might work short term, but seems like a bad idea to me.

Accompanist answered 24/5, 2022 at 16:50 Comment(0)
F
0

In my case, none of the above seemed to work. Until someone pointed me to the following website Check if TLS 1.2 is enabled

It seemed that the server only had records in registry for SSL 2.0 and SSL 3.0. After adding the records for TLS protocols, the problem disappeared.

Protocols

Frap answered 8/12, 2022 at 13:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.