带有证书的 WebRequest 在第一次调用时失败,并在所有后续调用中成功

Posted

技术标签:

【中文标题】带有证书的 WebRequest 在第一次调用时失败,并在所有后续调用中成功【英文标题】:WebRequest with certificate fails on first call and succeeds all subsequent calls 【发布时间】:2020-08-21 06:33:57 【问题描述】:

基础设施

我有一个从旧服务器迁移的旧 .NET Framework 应用程序。 它在 4.6.2 中运行在 Windows Server 2012 R2 上。

表单应用程序调用也在同一应用程序池下的同一服务器中运行的 WCF 服务,而 WCF 服务对外部服务进行 WebRequest 调用。

外部调用需要 SSL/TLS 握手证书。

在旧服务器中,证书附加到请求中,但在新服务器中,这不起作用,我们必须将它们添加到信任库中。这可能是以下问题的重要线索。

问题

WCF 服务调用外部服务,第一次调用(在部署或重新启动应用程序后)总是错误The request was aborted: Could not create SSL/TLS secure channel.

但是,所有后续调用都成功运行。 就好像证书在第一次调用时没有正确加载到用户配置文件中一样,但之后却是。

这会导致在部署新版本后出现问题,所有首次调用都失败,从而影响实时流量。 我们在负载均衡器下的 6 个机器上具有相同的配置,并且所有 6 个机器上的行为都相同。

我想解决这个问题,以便在第一次通话时通话成功。

代码

    public static Stream SendRequest(string url, string requestXml, string clientKeyBase64, string clientKeyPassword, int requestTimeout)
    
        Stream os = null;
        HttpWebRequest req = null;
        WebResponse resp = null;
        byte[] bytes;

        try
        
            req = (HttpWebRequest)WebRequest.Create(url);
            req.Timeout = requestTimeout;
            req.ContentType = "application/xml; charset=utf-8";
            req.Method = "POST";
            bytes = Encoding.ASCII.GetBytes(requestXml);
            req.ContentLength = bytes.Length;

            ServicePointManager.Expect100Continue = true;
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            if (!string.IsNullOrWhiteSpace(clientKeyBase64))
            
                var cert = new X509Certificate2(Convert.FromBase64String(clientKeyBase64), clientKeyPassword);
                req.ClientCertificates.Add(cert);
            
            using (os = req.GetRequestStream())
            
                os.Write(bytes, 0, bytes.Length);
            

            using (resp = req.GetResponse())
            
                using (var stream = resp.GetResponseStream())
                
                    MemoryStream memStream;
                    memStream = new MemoryStream();
                    stream.CopyTo(memStream);
                    memStream.Seek(0, SeekOrigin.Begin); // Required to reset stream pointer at the beginning
                    return memStream;
                
            
        
        catch (Exception ex)
        
            throw ex;
        
    

我尝试过的事情

这篇标题为“WCF Client: First call fails, second call succeeds” 的文章与证书相关,非常有前途。但是,使用 ServicePointManager.Expect100Continue = true; 绕过 HTTP 状态 100 的建议不起作用。

尝试将协议设置为 TLS 1.2: ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

尝试按照this suggestion设置X509KeyStorageFlags.MachineKeySet

根据相同的建议,尝试将应用程序池标识更改为 Network Service、LocalSystem 和 LocalService,但均无效。

其他信息

应用程序池是推荐的身份“ApplicationPoolIdentity”,并且“加载用户配置文件”设置设置为 true,否则不会加载证书。

我想不出下一步该做什么,需要一些帮助来理解和解决这种行为

【问题讨论】:

使用 Wireshark 等工具进一步了解握手失败的情况,从而更好地了解下一步该做什么。 由于部署新版本不会更新应用程序池,您是否尝试过部署后回收应用程序池或使用应用程序池初始化预热?听起来 schannel 在第一次通话时没有醒来。docs.microsoft.com/en-us/iis/get-started/whats-new-in-iis-8/… 感谢您的回答,已修复 【参考方案1】:

现在通过添加这些键似乎可以工作:

X509KeyStorageFlags.UserKeySet |                                                                                                                                 
X509KeyStorageFlags.MachineKeySet |                                                                                                                                  
X509KeyStorageFlags.PersistKeySet |                                                                                                        
X509KeyStorageFlags.Exportable

谢谢

【讨论】:

以上是关于带有证书的 WebRequest 在第一次调用时失败,并在所有后续调用中成功的主要内容,如果未能解决你的问题,请参考以下文章

调试失败的 HTTPS WebRequest

powershell 此代码块可用于允许Invoke-WebRequest使用没有可信证书颁发机构的SSL地址

Powershell调用webrequest不起作用

C#使用WebRequest调用WebApi的方法

使用WebRequest对象调用新浪天气预报<转>

C#实现SOAP调用WebService