Search code examples
.netwcf

.NET 8 WCF Invokes very slow using PeerTrust certificate validation


After upgrading a legacy .NET 4.8 system to .NET 8, when the system calls a WCF service using nettcp binding, we found calls to a WCF service execute very slow when hosted on Linux.

The same .NET 8 code running on Windows can 'ping' the server in say 20ms, but in Linux whether on metal or in a container takes 150ms to 'ping' (Literally a method that just returns true). The call and security enforcement otherwise works.

We use self signed certificates to host the WCF Service. which are in the TrustedPeople store. The certs are mounted into the client container via /root/.dotnet/corefx/cryptography/x509stores

The server is configured like this in .NET 4.8

 <behavior name="netTcpBindingBehavior">
  <callbackDebug includeExceptionDetailInFaults="true" />
  <clientCredentials>
    <clientCertificate findValue="CN=local.oursystem.test" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectDistinguishedName" />
    <serviceCertificate>
      <authentication certificateValidationMode="PeerTrust" />
    </serviceCertificate>
  </clientCredentials>
</behavior>

Solution

  • After much debugging, it was found that using CertificateValidationMode=PeerTrust runs very slow under Linux compared to Windows.

    Implementing a custom validator sped it up

      /// <summary>
      /// PeerTrust mode is slow in Linux, so implement our own validator
      /// </summary>
      private class WcfServerX509CertificateValidator : X509CertificateValidator
      {
          string _expectedThumbprint;
    
          public WcfServerX509CertificateValidator(string expectedThumbprint)
          {
              Guard.NullOrEmpty(expectedThumbprint, "Certificate thumbprint required");
              _expectedThumbprint = expectedThumbprint;
          }
    
          public override void Validate(X509Certificate2 certificate)
          {
              Guard.That(certificate.Thumbprint == _expectedThumbprint, () => $"Certificate was not issued by a trusted issuer. Received thumbprint={certificate.Thumbprint}, expected={_expectedThumbprint}");
          }
      }
    

    The ChannelFactory<T> is configured with this snippet

     var serviceCertificate = factory.Credentials.ServiceCertificate;
     serviceCertificate.SslCertificateAuthentication = new X509ServiceCertificateAuthentication();
     var sslCertificateAuthentication = serviceCertificate.SslCertificateAuthentication;
    
     sslCertificateAuthentication.CertificateValidationMode = X509CertificateValidationMode.Custom;              // PeerTrust is slow under Linux
     sslCertificateAuthentication.CustomCertificateValidator = new WcfServerX509CertificateValidator(serverCertificate.Thumbprint);