1

I have a WCF client (.NET 4.0) that calls goverment medical service over SOAP/SSL. It works for a first couple of hours and make a dozen or more successfull calls to service , and then it brekas with exception. After first exception WCF calls will not work until application is restarted.

It works at other instalations , and event works at same place but only if run od Win 10 PC, but it must work on server. The server in question is Windows 2012 with latest updates.

The exception is :

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.

which is caused by :

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

I looked at event log to search for SChanell errors but there are none. Then I found that it is possible to change logging level for SChanell : http://www.infralib.com/2016/11/schannel-event-logging-levels/

After that there are many many logs for SCHanell in event log (20 per sec), and this happens in same time with error in app but I can not guarantee because there are lots of them :

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.

There is nothing on Google for this particular error codes.

Because I can't test by myself (need smart card certificate from nurse), an it happens after 2-3 hours of usage I didt get a chance to get a Wireshark trace yet.

UPDATE 12.6.2020 after aplying suggestion :::::::::::::::::::::::::

This was my config :

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

I make it an wsHttpBinding with reliableSession as suggested :

 <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>

With this config I get exception : Binding validation failed because the WSHttpBinding does not support reliable sessions over transport security (HTTPS). The channel factory or service host could not be opened. Use message security for secure reliable messaging over HTTP.

I found what it is about here : How to enable Session with SSL wsHttpBinding in WCF

And made only possible solution from there , a change in security mode :

<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>

And with this config I get : AuthenticationException: A call to SSPI failed, see inner exception. INNER EXCEPTION MESSAGE :Win32Exception: The message received was unexpected or badly formatted (Probably because it expects credentials in returning message.)

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

I do not have control over service implementation (it is healthcare/goverment).

Is there any other way to stop expire of channel ?

I will mention again that this works on other instaltions/servers at other clients, it is something with this particular server or OS/.NET/update combination.

Is there any way of getting what is wrong wtih SChanel ?

Error 80 means "INTERNAL ERROR" for SChanell and that is only stuff you can google out.

UPDATE 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)
                    { ........

I added another call with another newly created client , and I get the same error. I wonder what is there in some GLOBAL state about SSL that it requires restart of application, everithing is recreatdd for second call, does WCF framework reuses channels in background or something similar ?

Domagoj
  • 11
  • 1
  • 5

1 Answers1

1

According to your description, the WCF client runs stable for the first few hours, and then an exception occurs. It is likely that the WCF client did not call the server for a certain period of time, resulting in the channel being shut down. At this point, an exception occurs when the client calls the server. By default, if the client does not call the server within 10 minutes, the channel is destroyed.

There are two solutions, one is to set a long expiration time on the server-side, the other is to catch this exception and recreate a channel when an exception occurs.

enter image description here

This is the WCF server configuration,where receivetimeout and inactivitytimeout are both client inactive timings.As long as one of them detects that the client is inactive for 10 minutes,the corresponding channel will be removed, resulting in an exception. Therefore, these two attributes should be set at the same time to increase the idle detection time.

For more information about InactivityTimeout,Please refer to the following link:

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

UPDATE

There is another method. You don't need to modify your configuration file. You just need to catch its exception when calling the server method.

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

Create a channel when there is an exception in calling the server-side method.

Ding Peng
  • 3,702
  • 1
  • 5
  • 8
  • You can catch this exception and recreate the channel every time you catch it. – Ding Peng Jun 15 '20 at 01:09
  • I updated question with results from implementig your suggestion – Domagoj Jun 19 '20 at 12:57
  • I think it's because of the problem caused by the channel expiration. There's another scheme that you can call the server regularly to avoid the channel expiration. You can call the server every 5 minutes. – Ding Peng Jun 23 '20 at 09:47