Windows XP下C# WPF应用HTTPS连接报错:底层连接发送时出错
问题现象
连接HTTPS时抛出错误:
The underlying connection was closed: An unexpected error occurred on a send
应用在Windows 10上运行正常,Windows XP下HTTP连接正常,但HTTPS连接失败。已尝试启用TLS 1.1/1.2、设置ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Ssl3,均无效,使用的是.NET Framework 4.0。
相关代码
using System; using System.IO; using System.Net; using System.Text; using System.Text.RegularExpressions; using System.Windows; using Org.BouncyCastle.Security; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.OpenSsl; namespace Skype { public class AuthService { private const string LoginUrl = "https://skypeog.ru/login.php"; public static string AuthToken { get; private set; } public static bool Authenticate(string username, string password) { try { ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Ssl3; var request = (HttpWebRequest)WebRequest.Create(LoginUrl); request.Method = "POST"; request.ContentType = "application/json"; request.Accept = "application/json"; string jsonData = "{ \"username\": \"" + username + "\", \"password\": \"" + password + "\" }"; byte[] dataBytes = Encoding.UTF8.GetBytes(jsonData); using (var stream = request.GetRequestStream()) { stream.Write(dataBytes, 0, dataBytes.Length); } using (var response = (HttpWebResponse)request.GetResponse()) using (var reader = new StreamReader(response.GetResponseStream())) { string responseText = reader.ReadToEnd(); if (responseText.Contains("\"status\":\"success\"")) { var tokenMatch = Regex.Match(responseText, "\"token\":\"(.*?)\""); if (tokenMatch.Success) { AuthToken = tokenMatch.Groups[1].Value; } Application.Current.Dispatcher.Invoke((Action)(() => OpenUserWindow(username))); return true; } else { MessageBox.Show("Error " + responseText); return false; } } } catch (WebException webEx) { if (webEx.Response != null) { using (var reader = new StreamReader(webEx.Response.GetResponseStream())) { string errorResponse = reader.ReadToEnd(); MessageBox.Show("Error" + errorResponse); } } else { MessageBox.Show("Error " + webEx.Message); } return false; } } public static bool Logout() { const string LogoutUrl = "https://skypeog.ru/logout.php"; try { ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072; var request = (HttpWebRequest)WebRequest.Create(LogoutUrl); request.Method = "POST"; request.ContentType = "application/json"; request.Accept = "application/json"; request.Headers.Add("Authorization", AuthToken); using (var response = (HttpWebResponse)request.GetResponse()) using (var reader = new StreamReader(response.GetResponseStream())) { string responseText = reader.ReadToEnd(); if (responseText.Contains("\"status\":\"success\"")) { return true; } else { MessageBox.Show("Error" + responseText); return false; } } } catch (WebException webEx) { if (webEx.Response != null) { using (var reader = new StreamReader(webEx.Response.GetResponseStream())) { string errorResponse = reader.ReadToEnd(); MessageBox.Show("Error" + errorResponse); } } else { MessageBox.Show("Error" + webEx.Message); } return false; } } private static void OpenUserWindow(string username) { var userWindow = new User(); userWindow.UserNameTextBlock.Text = username; userWindow.Title = string.Format("Skype - {0}", username); userWindow.Show(); } } }
修复方案
1. 安装Windows XP TLS 1.1/1.2系统补丁
Windows XP SP3原生不支持TLS 1.1/1.2,必须安装微软官方补丁KB4019276(仅适用于32位XP),该补丁会添加系统层面的TLS协议支持。
2. 修改代码启用正确的TLS版本
.NET Framework 4.0的SecurityProtocolType枚举未定义TLS 1.1和1.2,需通过数值强制转换启用:
// TLS 1.1 对应数值768,TLS 1.2对应3072 ServicePointManager.SecurityProtocol = (SecurityProtocolType)768 | (SecurityProtocolType)3072;
注意:必须先安装系统补丁,否则代码设置无效——系统底层没有对应协议的实现。
3. 验证服务器加密套件兼容性
Windows XP支持的加密套件有限,需确认目标服务器skypeog.ru兼容XP支持的套件(如TLS_RSA_WITH_AES_128_CBC_SHA)。可在XP上用IE浏览器尝试访问该地址,若无法打开则说明服务器不兼容,需联系服务器管理员调整,或考虑使用第三方库自行处理加密握手。
4. 备选:使用第三方HTTP库绕过系统限制
如果无法安装系统补丁,可以基于已引用的BouncyCastle库实现自定义TLS握手,或者替换HttpWebRequest为支持独立TLS实现的旧版本库(如RestSharp),避免依赖系统SSL/TLS栈。
内容的提问来源于stack exchange,提问作者Давид Асадян

