我有一个针对.NET 4.0的Windows Forms应用程序,该应用程序在具有.NET 4.0最新版本的x64 Windows Server 2012 R2服务器上运行。为了将请求发送到服务器(在全国范围内使用),您需要在请求的标头中添加会话令牌(从服务器获取)。根据官方文档从服务器获取令牌:
bool ServerCertificateBypass(object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
return true;
}
public string AcquireToken (X509Certificate userCertificate,
string password, string userName)
{
// configure general http options
ServicePointManager.ServerCertificateValidationCallback = ServerCertificateBypass;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 |
SecurityProtocolType.Tls; // default in .NET
// create https request
var url = String.Format("https://serveraddress/validator?username={0}", userName);
var request = (HttpWebRequest)WebRequest.Create(new Uri(url));
// configure web request
request.Accept = "*/*";
request.KeepAlive = true;
request.AllowAutoRedirect = false;
request.PreAuthenticate = true;
// set Internet Explorer proxy
request.Proxy = ProxyHelper.GetSystemWebProxy();
// add user certificate
request.ClientCertificates.Add(userCertificate); // userCertificate is a X509Certificate object
// add server credentials
var credentials = new CredentialCache();
credentials.Add(uri, "Basic", new NetworkCredential(userName.password));
request.Credentials = credentials;
// overwrite CookieContainer to store cookies
request.CookieContainer = CookieJar; // CookieJar is a static CookieContainer object
// get web response
var response = request.GetResponse();
// extract session token
return response.Headers["SESSION_TOKEN"];
}
ServerCertificateBypass是一个始终返回true的委托,而userName和password是字符串变量。会话令牌在一小时后过期,因此我决定使用内部计时器。每当它打勾时,我都会调用上面的函数并获取一个新令牌:
private void timer_Tick(object sender, EventArgs e)
{
lock (syncLock)
{
// stop timer
timer.Stop();
// acquire and store token
Task.Factory.StartNew(() => AcquireToken(Settings.UserCertificate, Settings.Password, Settings.UserName))
.ContinueWith(t =>
{
if (!string.IsNullOrEmpty(t.Result))
{
SaveToken(t.Result)
UpdateUI();
}
});
// start timer
timer.Start();
}
}
如果我将频率设置为5分钟,则在调用request.GetResponse()时,上面的代码在第6或第7个滴答处正常工作,并且出现以下错误:
请求已中止:无法创建SSL / TLS安全通道
如果我重新启动该应用程序,则可以再次正常运行几秒钟。如果我将频率设置为60分钟,则它仅会获得两个滴答声,此后它会因相同的错误而中断。我在UI中还有一个按钮可以手动调用AcquireToken函数。在我收到该错误之前,它一直可以正常工作,直到我重新启动该应用程序为止,该错误也将不起作用。
将ServicePointManager.SecurityProtocol设置为Tls1.1,Tls1.2的任何变体(如The request was aborted: Could not create SSL/TLS secure channel中的建议)不起作用,并且服务器不支持它,因为它不在官方文档中。
问题是,为什么它会在几次正常运行后中断,然后在重新启动应用程序后将无法工作?
感谢您的帮助。
更新:我忘了提到用户证书在2020年之前有效,并且Internet Explorer中启用了SSL3.0和TLS的每个版本。