首页 > 解决方案 > WCF 客户端 - 无法为 SSL/TLS 建立安全通道

问题描述

我有一个通过 SOAP/SSL 调用政府医疗服务的 WCF 客户端 (.NET 4.0)。它在最初的几个小时内工作,并成功拨打了十几次或更多次 service 电话,然后它异常中断。在第一次出现异常后,WCF 调用将无法工作,直到应用程序重新启动。

它适用于其他安装,并且事件在同一个地方工作,但前提是运行 od Win 10 PC,但它必须在服务器上工作。有问题的服务器是具有最新更新的 Windows 2012。

例外是:

SecurityNegotiationException: Could not establish secure channel for SSL/TLS with authority 'ws.cezih.hr:6443'.
INNER EXCEPTION MESSAGE :WebException: The request was aborted: Could not create SSL/TLS secure channel.

这是由以下原因引起的:

AuthenticationException: A call to SSPI failed, see inner exception.
INNER EXCEPTION MESSAGE :Win32Exception: The Local Security Authority cannot be contacted

我查看了事件日志以搜索 Schanell 错误,但没有。然后我发现可以更改 Schanell 的日志记录级别:http: //www.infralib.com/2016/11/schannel-event-logging-levels/

之后,事件日志中有许多 SCHanell 的日志(每秒 20 条),这与应用程序中的错误同时发生,但我不能保证,因为它们有很多:

A fatal alert was generated and sent to the remote endpoint. This may
result in termination of the connection. The TLS protocol defined
fatal error code is 80. The Windows SChannel error state is 301.

谷歌上没有这个特定的错误代码。

因为我不能自己测试(需要护士的智能卡证书),它会在使用 2-3 小时后发生,我还没有机会获得 Wireshark 跟踪。

应用建议后更新 12.6.2020:::::::::::::::::::::::::

这是我的配置:

<basicHttpBinding>
    <binding name="osiginfo">
        <security mode="Transport">
           <transport clientCredentialType="Certificate"></transport>
        </security>
    </binding>
</basicHttpBinding>

我按照建议将其设置为带有可靠会话的 wsHttpBinding :

 <wsHttpBinding>
    <binding name="osiginfo" receiveTimeout="24:00:00">
      <security mode="Transport">
        <transport clientCredentialType="Certificate"></transport>
      </security>
      <reliableSession enabled="true" inactivityTimeout="24:00:00"/>
    </binding>
</wsHttpBinding>

使用此配置,我得到异常:绑定验证失败,因为 WSHttpBinding 不支持通过传输安全 (HTTPS) 进行的可靠会话。无法打开通道工厂或服务主机。使用消息安全性通过 HTTP 进行安全可靠的消息传递。

我在这里找到了它的含义:How to enable Session with SSL wsHttpBinding in WCF

并从那里做出唯一可能的解决方案,即安全模式的改变:

<wsHttpBinding>
    <binding name="osiginfo" receiveTimeout="24:00:00">
      <security mode="TransportWithMessageCredential">
        <transport clientCredentialType="Certificate"></transport>
      </security>
      <reliableSession enabled="true" inactivityTimeout="24:00:00"/>
    </binding>
</wsHttpBinding>

使用此配置,我得到: AuthenticationException:对 SSPI 的调用失败,请参阅内部异常。内部异常消息:Win32Exception:收到的消息是意外的或格式错误 (可能是因为它在返回消息时需要凭据。)

当然以以下结尾:SecurityNegotiationException:无法为具有权限“ws.cezih.hr:6443”的 SSL/TLS 建立安全通道。内部消息:WebException:请求被中止:无法创建 SSL/TLS 安全通道。

我无法控制服务实施(它是医疗保健/政府)。

有没有其他方法可以阻止频道过期?

我将再次提到,这适用于其他客户端的其他安装/服务器,它与此特定服务器或 OS/.NET/更新组合有关。

有什么方法可以解决 SChanel 的问题吗?

错误 80 表示 Schanell 的“内部错误”,这只是你可以用谷歌搜索出来的东西。

更新 19.6.2020::::::::::::::::::

                 try {
                CezihOsigInfo.osigInfoForBISResponseOsigInfoForBISOutput[] response = null;
                using (CezihOsigInfo.osiginfoPortTypeClient client = new CezihOsigInfo.osiginfoPortTypeClient())
                {
                    client.ClientCredentials.ClientCertificate.Certificate = DohvatiKlijentskiCertifikat();

                    base.PostaviDodatnePodatkeCertifikata(client?.ClientCredentials?.ClientCertificate?.Certificate);
                    try
                    {
                        base.ZapisiSlanje(string.Format("Dohvat informacija o osiguranju pacijenta: {0}", mbo), "DohvatiOsiguranikaPrekoMBO");
                        response = client.osigInfoForBIS(mbo);

                        base.ZapisiPrimanje(string.Format("Dohvaćeni podaci o osiguranju pacijenta: {0}", mbo), StatusPrijenosa.DohvatiPrekoPK(100));
                        base.ZapisiLog();
                    }
                    catch (System.ServiceModel.Security.SecurityNegotiationException ex)
                    {
                        //try one more call 
                        using (CezihOsigInfo.osiginfoPortTypeClient client2 = new CezihOsigInfo.osiginfoPortTypeClient())
                        {
                            client2.ClientCredentials.ClientCertificate.Certificate = DohvatiKlijentskiCertifikat();

                            try
                            {
                                base.ZapisiSlanje(string.Format("Dohvat informacija o osiguranju pacijenta DRUGI POKUSAJ: {0}", mbo), "DohvatiOsiguranikaPrekoMBO");
                                response = client2.osigInfoForBIS(mbo);

                                base.ZapisiPrimanje(string.Format("Dohvaćeni podaci o osiguranju pacijenta DRUGI POKUSAJ: {0}", mbo), StatusPrijenosa.DohvatiPrekoPK(100));
                                base.ZapisiLog();
                            }
                            catch {
                                base.ZapisiPrimanje(string.Format("Greška prilikom dohvata informacija o osiguranjima pacijenta DRUGI POKUSAJ: {0}", mbo), StatusPrijenosa.DohvatiPrekoPK(1));
                                base.ZapisiLog();

                                throw new ObavijesnaIznimka("Dogodila se greška prilikom poziva servisa (DRUGI POKUSAJ). " + (ex.InnerException != null ? ex.InnerException.Message : ex.Message));
                            }
                        }

                    }
                    catch (Exception ex)
                    { ........

我用另一个新创建的客户端添加了另一个调用,我得到了同样的错误。我想知道在一些关于 SSL 的 GLOBAL 状态中有什么需要重新启动应用程序,所有内容都是 recreatdd 以进行第二次调用,WCF 框架是否在后台重用通道或类似的东西?

标签: .netwcfsslsoaphttps

解决方案


根据您的描述,WCF客户端前几个小时运行稳定,然后出现异常。很可能是WCF客户端一段时间没有调用服务端,导致通道被关闭。此时客户端调用服务端出现异常。默认情况下,如果客户端在 10 分钟内没有调用服务器,则通道被销毁。

解决方案有两种,一种是在服务端设置一个较长的过期时间,另一种是捕获这个异常,当异常发生时重新创建一个通道。

在此处输入图像描述

这是WCF服务端的配置,receivetimeout和inactivitytimeout都是client不活动的时间,只要其中一个检测到client不活动10分钟,就会移除对应的channel,导致异常。因此,应同时设置这两个属性,以增加空闲检测时间。

有关 InactivityTimeout 的更多信息,请参考以下链接:

https://docs.microsoft.com/en-us/dotnet/api/system.servicemodel.reliablesession.inactivitytimeout?view=netframework-4.8

更新

还有另一种方法。您无需修改​​配置文件。您只需要在调用服务器方法时捕获它的异常。

            try
            {
                service1Client.GetUserData("TEST");
            }
            catch (Exception e) {
                service1Client = new Service1Client();
                service1Client.GetUserData("TEST");
            }

调用服务端方法出现异常时创建通道。


推荐阅读