Search code examples
c#wcfapp-configclient-side

Need How To help moving system.serviceModel config section into my code


I have developed a Client/Server application for my current employer and one of the requirements is that the client part be distributed as a DLL. Since DLLs don't 'do' dllName.dll.config files, I need to move the config into the code. I have done this to the best of my ability, but I'm now getting this exception;

The X.509 certificate CN=ComputerName chain building failed.
The certificate that was used has a trust chain that cannot be verified. Replace the certificate or change the certificateValidationMode. An internal certificate chaining error has occurred.

I have googled, and otherwise searched all over for a resolution to this, but so far I can't find anything with a clear enough explaination of the various close-but-not-really-it solutions I've seen.

Anyway, there is a certificate in My LocalStore, but it's really only used by the server (currently testing both client and server on the same XP Pro box), at least, as far as I understand things. (Still pretty new to WCF) (Note how the client config file below does not get or otherwise point out the aforementioned cert to the client whatsoever.

The client's app.config looks like the following, and works just great via our test EXE, which contains the section of this config file.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
 <system.serviceModel>
  <bindings>
   <netTcpBinding>
    <binding name="MyTcpBinding_IFileXferService" 
       receiveTimeout="02:00:00" 
       sendTimeout="02:00:00" 
       transferMode="Streamed" 
       maxBufferSize="65536" 
       maxReceivedMessageSize="2147483647">
     <readerQuotas maxStringContentLength="2147483647" 
          maxArrayLength="2147483647" 
          maxBytesPerRead="65536" />

     <security mode="Transport">
      <transport clientCredentialType="None" />
     </security>

    </binding>
   </netTcpBinding>
  </bindings>

  <behaviors>
   <endpointBehaviors>
    <behavior name="ClientConfigBehavior">
     <dataContractSerializer maxItemsInObjectGraph="6553600" />
     <clientCredentials>

      <serviceCertificate>
       <authentication certificateValidationMode="None" />
      </serviceCertificate>

     </clientCredentials>
    </behavior>
   </endpointBehaviors>
  </behaviors>

  <client>
   <endpoint name="ClientConfig" 
       binding="netTcpBinding" 
       bindingConfiguration="MyTcpBinding_IFileXferService" 
         behaviorConfiguration="ClientConfigBehavior" 
       contract="ServiceRefs.IFileXferService" />
  </client>

 </system.serviceModel>

</configuration>

But now that I've moved the configuration information into the code (so that the DLL can be distributed without a .config file), I am, again, getting the exception mentioned above. (By the way, if we figure out that a given setting needs to be changable, I can add it to our own xml config file.)

The code meant to replace the above config looks like this:

 ...
 netTcpBinding = new NetTcpBinding();
 endpointAddr = new EndpointAddress(HostURI);

 updateMaxItemsBehavior(); // method below this snippet

 TimeSpan tsTwoHours = new TimeSpan(2,0,0);
 netTcpBinding.ReceiveTimeout = tsTwoHours;
 netTcpBinding.SendTimeout = tsTwoHours;

 netTcpBinding.TransferMode = TransferMode.Streamed;
 netTcpBinding.MaxBufferSize = 65536;
 netTcpBinding.MaxReceivedMessageSize = 2147483647;

 netTcpBinding.ReaderQuotas.MaxStringContentLength = 2147483647;
 netTcpBinding.ReaderQuotas.MaxArrayLength = 2147483647;
 netTcpBinding.ReaderQuotas.MaxBytesPerRead = 65536;

 // double the 64k default
 netTcpBinding.MaxBufferPoolSize = 131072;

 netTcpBinding.Security.Mode = SecurityMode.Transport;
 netTcpBinding.Security.Transport.ClientCredentialType =
  TcpClientCredentialType.None;

 // now to instantiate the service proxy client 
 svcClient = new FileXferServiceClient(netTcpBinding, endpointAddr);

I've tried this with various combinations of these next bits, but can't get around the dang exception.

 // Set the certificate for the client.
 X509Certificate2 cert = new X509Certificate2();
 svcClient.ClientCredentials.ClientCertificate.Certificate = cert;

 //svcClient.ClientCredentials.ClientCertificate.SetCertificate(
 //    StoreLocation.LocalMachine, 
 //    StoreName.My, 
 //    X509FindType.FindByThumbprint,
 //    "ThumbprintThumbprintThumbprintThumbprint");

 // here's this
 //
 private void updateMaxItemsBehavior()
 {
  svcEndpoint = new ServiceEndpoint(
   new ContractDescription("IFileXferService"));

  foreach (OperationDescription operation in
   svcEndpoint.Contract.Operations)
  {
   operation.Behaviors
    .Find<DataContractSerializerOperationBehavior>()
     .MaxItemsInObjectGraph = 6553600;
  }
 }

In summary, our test Winforms app (and thus the client-side of our WCF app) works fine with the config settings in the wfappname.exe.config file, but now that I've tried to move this all into code, it's apparently demanding a certificate. I guess what I'm really hoping for is 'how do I duplicate the above app.config in code so that the app.config is not needed by my WCF Client.'

Can anyone help me with this? (I'll be yer best friend!) ; )

Thanks, Scott


Solution

  • I feel ya man.

    I don't like the idea that I have to embed the .config stuff into web.config when a WCF service is deployed into IIS, for example, or translate the .config and embed into code when hosting in some other way.

    The way I worked around it was to use a custom service host that overrides the default ApplyConfiguration() logic. It allows, on the service side, the WCF DLL to look in dllname.dll.config (or anyplace, really) for its configuration.

    It is all explained here. That information is also duplicated in this answer on Stackoverflow.

    You're using a WCF client, so you need to override the ApplyConfiguration() method in a custom ChannelFactory. This is described here. On that same forum post, there is an alternative technique described where you override CreateDescription() and specify an alternative .config file path. These approaches are very similar; either should work in a WCF client.

    You might also consider embedding the .config as a resource embedded into the .dll. That way, you still have a single-file distribution (the dll), but you can use the xml config syntax to specify everything. At build time, embed the .config file into the DLL. At runtime inside your custom ApplyConfiguration(), you'd have to call Assembly.GetManifestResourceStream();, passing the string "dllname.dll.config" to get a read-only stream that contains the config information.

    If you want to allow the user to override the embedded config, then you could introduce that intelligence into your own ApplyConfiguration() implementation. Probably look for the filesystem file first; if it is not present, then fallback to the embedded .config file.

    This answer doesn't really tell you how to move config settings into code, because I think it is a heinous idea. But maybe it solves your problem anyway.