"Базове з'єднання було закрито: при надсиланні сталася несподівана помилка." З SSL-сертифікатом


119

Проблема: я отримую цей виняток: "НЕЗАДАЧНА ПІДТРИМКА ЗАКРИТАЄТЬСЯ: НЕОЧАКОВАНА ПОМИЛКА ВІДКРИТИ НАДІСЛЕННЯ" в моїх журналах, і це порушує нашу інтеграцію OEM з нашою системою маркетингу електронної пошти у випадкові часи, що змінюються від [1 година - 4 години]

Мій веб-сайт розміщений на сервері Windows 2008 R2 з IIS 7.5.7600. На цьому веб-сайті є велика кількість компонентів OEM та всеосяжна інформаційна панель. Все добре працює з усіма іншими елементами веб-сайту, за винятком одного з компонентів маркетингу електронної пошти, який ми використовуємо як рішення iframe на нашій панелі приладів. Як це працює, я надсилаю httpWebRequestobject з усіма обліковими записами, і я отримую URL-адресу, яку я вкладаю в iframe, і вона працює. Але це працює лише деякий час [1 годину - 4 години], і тоді я отримую нижченаведене виняток: "НЕВЕРДІЛЬНЕ ПІДТРИМКА ЗАКРИТО: НЕОЧАКОВАНА ПОМИЛКА ВІДКРИТИ НАДІСЛЕНО", і навіть якщо система намагається отримати URL-адресу від httpWebRequest it не вдається за тим же винятком. Єдиний спосіб змусити його знову працювати - це переробити пул додатків або що-небудь відредагувати в web.config.

Варіант спробували

Явно додано, keep-alive = false

keep-alive = true

Збільшено час очікування: <httpRuntime maxRequestLength="2097151" executionTimeout="9999999" enable="true" requestValidationMode="2.0" />

Я завантажив цю сторінку на веб-сайт, що не належить до SSL, щоб перевірити, чи SSL сертифікат на нашому виробничому сервері робить з'єднання, щоб трохи відмовитись.

Будь-який напрямок до розв'язання дуже вдячний

Код:

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`

Ви коли-небудь це розуміли?
Бретт G

11
так, мені вдалося змусити його працювати з цим кодом ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls Або SecurityProtocolType.Ssl3
Arvind Morwal

Я збирався померти за те саме питання. Мене кілька годин боролися з тим же питанням. Дякую за коментарі, це врятувало мені день.
Самерс Джавед

2
@ user3458212 ви можете додати свій коментар як відповідь
icc97

2
У моєму випадку, за допомогою веб-сайту в Visual Studio 15 все йде нормально, але наприкінці, тому що я не можу оновити фреймворк на сервері, а примушування TLS 1.2 та відключення режиму живого не працює, мені довелося налаштувати проміжний веб-сервер для проксі цільового веб-сервера, який припиняє з'єднання.
Жозе Роберто Гарсія Чіко

Відповіді:


193

Для мене це було tls12:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

42
Зауважте, що ви повинні бути обережними, оскільки ця зміна є глобальною для вашої AppDomain і призведе до відмови на будь-який веб-сайт, який не пропонує TLS 1.2, (що ви можете віддати перевагу, якщо дані, що перевозяться, справді чутливі). Щоб віддати перевагу TLS 1.2, але все-таки дозволяти 1.1 та 1.0, ви повинні АБО:ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
Dusty

тут же ти врятував мені життя, витратив стільки часу, щоб з’ясувати, що сталося з RestSharp
darul75

Це рішення також працює для тих, хто не використовує RestSharp, а також для тих, хто не використовує ServicePointManager. Просто скопіюйте та вставте вищевказаний рядок перед вашим викликом WebRequest або тим, що ви використовуєте, щоб зробити запит. Я спочатку ігнорував це рішення через вищезазначені причини.
goku_da_master

або просто додати його до того, що вже є ... System.Net.ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
uosjead

8
Для цього в PowerShell, "бінарних" або "їх разом так":[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls
Adam S

62

Якщо ви застрягли в .Net 4.0 і цільовий сайт використовує TLS 1.2, вам потрібен наступний рядок. ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;

джерело: TLS 1.2 та підтримка .NET: як уникнути помилок підключення


5
Дивовижно! Я просто додам, що (SecurityProtocolType)768можна використовувати для "Tls11" (тобто TLS 1.1).
Соломон Руцький

2
Це справді допомагає. Це врятувало мені день. Я повинен дотримуватися .Net 2.0.
Хао Нгуен

22

Код нижче вирішив проблему

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls Or SecurityProtocolType.Ssl3

5
Однак це матиме на увазі, що ServicePointManager.SecurityProtocolстатичний об'єкт означає, що зміна цього значення вплине на всі підрядки WebRequestабо WebClientвиклики. Ви можете створити окремо, AppDomainякщо хочете ServicePointManagerмати різні налаштування. Дивіться stackoverflow.com/questions/3791629/… для більш детальної інформації.
stack247

1
Корисне додаткове читання, щоб зрозуміти, що робить цей код: stackoverflow.com/questions/26389899/…
Джон Шнайдер

@Marnee Я помістив його в корінь композиції моєї програми, тому він встановлюється перед тим, як виникають введення-виведення
JG в SD

@Marnee Помістіть його в статичний конструктор, щоб він виконувався рівно один раз, коли вперше доступ до класу. Мені довелося включити всі протоколи, щоб охопити всі випадки.
Nyerguds

14

У мене з цим питанням вже кілька днів є інтеграція, яка також "раніше працювала".

Із чистої депресії я просто спробував

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

Це вирішило це для мене. Навіть хоча інтеграція суворо використовує лише SSLv3.

Я прийшов до усвідомлення того, що щось не буде, оскільки Фіддлер повідомив, що є "порожній шифр для переговорів щодо TLS" або щось подібне.

Сподіваємось, це працює!


Зауважте, що навіть якщо вашому коду не потрібна TLS, якщо сервер, з яким він спілкується, DOES, він спробує домовитись з TLS і провалиться, якщо не зможе. Порожній шифр узгодження TLS був слотом, який очікував обміну протоколом TLS, який ви зрештою надали. Це, ймовірно, "раніше працювало", оскільки адміністратор сервера, ймовірно, щойно включив TLS на сервері, з яким спілкувався ваш додаток.
vapcguy

13

У моєму випадку сайт, до якого я підключаюсь, оновив до TLS 1.2. В результаті мені довелося встановити .net 4.5.2 на свій веб-сервер, щоб підтримати його.


Чи можна це зробити на рівні реєстру на машині? Я вимкнув усі протоколи SSL, залишивши TLS 1.0, 1.1, 1.2, проте, наскільки я розумію, незабаром слід видалити що-небудь менше, ніж TLS 1.2, щоб бути сумісним з PCI.
brendo234

13

Перейдіть на веб-сторінку web.config / App.config, щоб перевірити, який час виконання .net ви використовуєте

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

Ось таке рішення:

  1. .NET 4.6 і вище. Вам не потрібно робити додаткових робіт для підтримки TLS 1.2, він підтримується за замовчуванням.

  2. .NET 4.5. TLS 1.2 підтримується, але це не протокол за замовчуванням. Щоб використовувати його, потрібно увімкнути його. Наступний код зробить TLS 1.2 за замовчуванням, переконайтеся, що виконати його, перш ніж встановити з'єднання із захищеним ресурсом:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12

  1. .NET 4.0. TLS 1.2 не підтримується, але якщо у вас встановлено .NET 4.5 (або вище), ви все одно можете ввімкнути TLS 1.2, навіть якщо ваша програма не підтримує його. Єдина проблема полягає в тому, що SecurityProtocolType в .NET 4.0 не містить записи для TLS1.2, тому нам доведеться використовувати числове представлення цього значення перерахунку:

ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;

  1. .NET 3.5 або нижче. TLS 1.2 не підтримується (*) і немає вирішення. Оновіть додаток до більш нової версії фреймворку.

6

Я виявив, що це знак того, що на сервері, де ви розгортаєте код, встановлена ​​стара .NET Framework, яка не підтримує TLS 1.1 або TLS 1.2. Кроки для виправлення:

  1. Встановлення останнього .NET Runtime на ваші виробничі сервери (IIS & SQL)
  2. Встановлення останнього пакета розробника .NET на ваші розроблювальні машини.
  3. Змініть налаштування "Цільовий фреймворк" у своїх проектах Visual Studio на останню .NET структуру.

Ви можете отримати найновіший пакет для розробників .NET і час виконання за цією URL-адресою: http://getdotnet.azurewebsites.net/target-dotnet-platforms.html


Змінив цільову рамку з 4.5.2 на 4.6.1 і почав працювати, дякую Патріку.
Vivek Sharma

4

У нас виникла ця проблема, коли веб-сайт, що здійснював доступ до нашого API, отримував "Основне з'єднання було закрито: при надсиланні сталася несподівана помилка." повідомлення.

Їх код був поєднанням .NET 3.x та 2.2, що, наскільки я розумію, означає, що вони використовують TLS 1.0.

Відповідь нижче може допомогти вам діагностувати проблему, включивши TLS 1.0, SSL 2 та SSL3, але щоб бути дуже зрозумілим, ви не хочете цього робити довгостроково, оскільки всі три протоколи вважаються небезпечними і більше не повинні бути б / в :

Щоб наш IIS відповідав на їх API-виклики, нам довелося додати параметри реєстру на сервері IIS, щоб явно включити версії TLS - ПРИМІТКА. Після внесення цих змін потрібно перезапустити сервер Windows (а не лише службу IIS):

[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

Якщо цього не зробити, ви також можете експериментувати з додаванням запису для 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

Щоб було зрозуміло, це не приємне рішення , і правильне рішення - змусити абонента використовувати TLS 1.2, але вищесказане може допомогти встановити діагноз, що це проблема.

Ви можете пришвидшити додавання цих записів у регістр за допомогою цього сценарію повноважень:

$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

Це модифікована версія сценарію зі сторінки довідки Майкрософт для налаштування TLS для VMM . Ця стаття basics.net була сторінкою, яка спочатку дала мені ідею переглянути ці налаштування.


Наша проблема полягала у випуску трубопроводу через Team City, який раптово зупинився, коли ми змінили сертифікат для вашого живого сервера. Ми вже змінили сервер, щоб використовувати тільки TLS1.2, і наш трубопровід Team City перестав працювати ... Працював як мрія ... додав записи в регістр і перезапустив сервер ... БУМ !!! Дякую tomRedox !!
Gwasshoppa

@Gwasshoppa, лише щоб повторити, що вищезазначене - лише зупинка для діагностики проблеми. Тепер ви знаєте, що проблемою версії TLS є рішення змінити конвеєр випуску, щоб він міг працювати з TLS1.2, а потім знову відключити TLS <1.2 та SSL 2 та 3. Відповідь вище я трохи оновив, щоб також наголосити.
tomRedox

4

Просто додайте:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;


1
Будь ласка, надайте пояснення зі своєю відповіддю.
Word Rearranger

4
Ця відповідь також нічого не додає до попередніх.
Арвінд Мані

2

Якщо хтось допомагає, у нас виникла проблема з відсутнім сертифікатом. Середовище - це стандарт Windows Server 2016 із .Net 4.6.

Існує самостійний сервіс WCF https URI, для якого Service.Open () виконується без помилок. Інший потік продовжує отримувати доступ до https: // OurIp: 443 / OurService? Wsdl щоб переконатися, що послуга була доступною. Доступ до WSDL, який не вдався:

Базове з'єднання було закрито: при надсиланні сталася несподівана помилка.

Використання ServicePointManager.SecurityProtocol із застосованими налаштуваннями не працює. Гра з ролями та функціями сервера також не допомогла. Потім ступив до Джайз Джордж , SE, вирішивши це питання за пару хвилин. Джайз встановив сертифікат самопідписання в IIS, підтверджуючи проблему. Ось що він зробив для вирішення цього питання:

(1) Відкрийте менеджер IIS (inetmgr) (2) Клацніть на вузлі сервера на лівій панелі та двічі клацніть «Сертифікати сервера». (3) Клацніть на "Створити самопідписаний сертифікат" на правій панелі та введіть усе, що вам потрібно для дружнього імені. (4) Клацніть на "Веб-сайт за замовчуванням" на панелі ліворуч, натисніть "Прив'язки" на правій панелі, натисніть "Додати", виберіть "https", виберіть створений вами сертифікат і натисніть "ОК" (5) Доступ https URL, вона повинна бути доступною.


Ви б могли подумати, що адміністратор сервера додав би сервер SSL! D'oh! lol :) Я можу бачити, що це відбувається, але, можливо, більш сприятливим сценарієм може стати термін дії, який закінчився, що минув, що потребує оновлення.
vapcguy

2

Ви просто змінюєте версію програми, на зразок 4,0 до 4,6, і публікуєте цей код.

Також додайте нижче рядки коду:

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

1

Використання проксі-сервера налагодження HTTP може спричинити це - наприклад, Fiddler.

Я завантажував сертифікат PFX з локального файлу (автентифікація на Apple.com), і це не вдалося, оскільки Fiddler не зміг передати цей сертифікат.

Спробуйте відключити Fiddler для перевірки, і якщо це рішення, тоді вам, ймовірно, потрібно встановити сертифікат на вашу машину або якимось чином Fiddler може ним користуватися.


0

Нижче наведений код вирішив мою проблему:

request.ProtocolVersion = HttpVersion.Version10; // THIS DOES THE TRICK
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.