带有挂在 GetContext() 上的 SSL 证书的 HttpListener

HttpListener with SSL certificate hanging on GetContext()

我创建了一个 windows 服务,它使用 HttpListener 响应一些使用 .NET Framework 4.0 的第三方请求。侦听器通过以下方式绑定到 https 前缀:

listener.Prefixes.Add("https://+:" + Properties.Settings.Default.ListeningPort.ToString() + "/");

该服务还通过以下方式在计算机商店中自行注册 https 证书:

X509Certificate2 certificate = new X509Certificate2(Properties.Settings.Default.HttpsCertPath, "", X509KeyStorageFlags.MachineKeySet);
X509Store store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
store.Add(certificate);
store.Close();

此外,该服务还注册了证书 - ip:port 绑定在 Http API 中使用以下内容:

ProcessStartInfo psi = new ProcessStartInfo();            
psi.FileName = "netsh";
psi.Arguments = "http add sslcert ipport=0.0.0.0:" + Properties.Settings.Default.ListeningPort.ToString() + " certhash=" + certificate.Thumbprint + " appid=" + appId;
Process proc = Process.Start(psi);                
proc.WaitForExit();
psi.Arguments = "http add sslcert ipport=[::]:" + Properties.Settings.Default.ListeningPort.ToString() + " certhash=" + certificate.Thumbprint + " appid=" + appId;
Process proc = Process.Start(psi);
proc.WaitForExit();

一切正常,正如预期的那样......除了......(邪恶的部分来了): 在 运行 一段时间后,一个小时左右,listener.GetContext() 不再 returns 并且客户端因 "connection reset" 之类的错误而被丢弃。

经过漫长而痛苦的调查,我发现错误是在 HTTP API 级别,并且在 SSL 握手期间会发生故障,因此 listener.GetContext() 根本没有返回。更重要的是,我发现对于每个失败的请求,在 Windows 事件日志的系统日志中,都会记录以下错误:

Source Schannel: A fatal error occurred when attempting to access the SSL server credential private key. The error code returned from the cryptographic module is 0x8009030D. The internal error state is 10001.

Source HttpEvent: An error occurred while using SSL configuration for endpoint 0.0.0.0:9799.  The error status code is contained within the returned data.

进一步深入研究问题后,我注意到有一次私钥从安装的证书中消失了。结合我在 this link 上阅读的内容,让我有了不同的想法。所以我试了一下:

X509Certificate2 certificate = new X509Certificate2(Properties.Settings.Default.HttpsCertPath, "", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);

基本上,我添加了 X509KeyStorageFlags.Exportable(还记得如果您没有在 IIS 上将证书标记为可导出的,则在创建 SSL 绑定时遇到的错误吗?)和 X509KeyStorageFlags.PersistKeySet 标志. 这似乎已经完成了工作。该服务已经 运行 将近两天了,并且仍然很高兴地接受传入连接。

希望这能让别人免于遇到我遇到的麻烦。