none
Help with Certificate Authentication and Transport Security in WCF RRS feed

  • Question

  • Anyone that has spent anytime researching bindings in WCF knows that half the information on the web is crap.
    I have spent the last 2 weeks attempting to get Certificate Authentication with Transport Security working.
    The sad part is that at one time I had it working perfectly.  Now its not working period.
    I am only getting the dreaded message

    The HTTP request was forbidden with client authentication scheme 'Anonymous'.

    Yes I have read both MSDN articles

    MSDN article MSDN article

    I also know that both IIS and the virtual directory are setup correctly,

    I am asking for help in the WCF forum and everything you need to help me resolve this issue is in the drop box Url

    Drop Box URL

    At the URL is a zip that contains a .NET 4.0 console application along with a .NET 4.0 WCF web service.  There are NO binary objects in the ZIP.

    The web service currently listens on three endpoints.  I only need the basicTransportBinding and/or the wsTransportSecurityBinding endpoints to work.  The clientCredentialType on these endpoints is currently set to None and anyone familiar with the proper configuration knows to set the clientCredentialType="Certificate" in the endpoint bindings followed by the SSL Settings in IIS to Require SSL with Client certificates set to Require.

    To prove that my certificates work as expected turn off Require SSL in IIS and test using the wsHttpMessageSecurityBinding endpoint.

    I also included certificates in the zip that can be used to setup your PC for SSL.

    ClientTest2WaySSLService is the console application and it contains a folder named certificates.
    In that folder is a client certificate named Certificate_genericsigning.pfx that can be used on the client.
    In the folder LocalHostSSL are the following:
    Certificate_CA.cer - is a self signed certificate authority certificate.
    Certificate_CA.crl - is a certificate revocation list for the above certificate.
    Certificate_localhost.pfx - is a certificate that can be used for SSL using the host name of localhost.

    You setup your PC with these certificates as follows:
    Do all of the below using the (Local Computer store location)
    To setup IIS for SSL.
    Add the files Certificate_CA.cer and Certificate_CA.crl to your Trusted Root Certification Authorities store.
    This will add an CA Certificate entry in this store as well as an entry in the Certificate Revocation List store (a refresh will be required)
    Add the file Certificate_localhost.pfx to your Personal store.
    Go to your Default Web Site in IIS and change your bindings.
    Edit the http binding and set the host name to localhost
    Edit the https binding and set the certificate to localhost
    You are now setup for SSL using the provided certificates

    Add the file Certificate_genericsigning.pfx to your personal certificate store.

    All of the enclosed certificates use the same certificate authority 'CA Certificate' which you added first.
    All of these certificates were created using OpenSSL and I use these types of certificates extensively in my WCF testing in our development environment.


    Thanks in advance.

    Wednesday, August 7, 2013 2:31 AM

Answers

  • Ok the problem partially solved.

    The problem is in the signing certificate I was using on the client side.

    Its a valid certificate for WIF, SSL and wsHttpBinding but it does not pass the mustard for 2 Way SSL via  IIS.

    I replaced my client side certificate with a valid VeriSign signing certificate and finally got everything to work.  So once I analyse the contents of the certificate I should be able to make my own signing certificate using OpenSSL.

    Once that process is complete I will post what properties are required within a certificate to make it work for with 2 Way SSL.

    In case eveyone is wondering why OpenSSL its because we create and store all  certificates in a database so that they can be revoked, downloaded or recreated for testing purposes.


    bmukes


    Wednesday, August 14, 2013 9:23 PM

All replies

  • Not sure how this is going to be more help than the zip inside of drop box that I mentioned in my origional post but here you go.

    Please notice that tracing is turned on.

    *** Client app.config ***

    <configuration>
      <system.serviceModel>
    
        <client>      
          <endpoint name="basicTransportBinding" address="https://localhost/Test2WaySSLService/Service1.svc" binding="basicHttpBinding" bindingConfiguration="basicTransportBinding" contract="ClientTest2WaySSLService.IService1"/>
          <endpoint name="wsTransportSecurityBinding" address="https://localhost/Test2WaySSLService/Service1.svc/wsTransportSecurityBinding" binding="wsHttpBinding" bindingConfiguration="wsTransportSecurityBinding" contract="ClientTest2WaySSLService.IService1"/>
          <endpoint name="wsHttpMessageSecurityBinding" address="http://localhost/Test2WaySSLService/Service1.svc/wsHttpMessageSecurityBinding" binding="wsHttpBinding" bindingConfiguration="wsHttpMessageSecurityBinding" contract="ClientTest2WaySSLService.IService1"  behaviorConfiguration="ClientCertificateBehavior"/>
        </client>
        <bindings>
          <basicHttpBinding>
            <binding name="basicTransportBinding">
              <security mode="Transport">
                <transport clientCredentialType="Certificate" />
              </security>
            </binding>
            </basicHttpBinding>
          <wsHttpBinding>
            <binding name="wsTransportSecurityBinding">
              <security mode="Transport">
                <transport clientCredentialType="Certificate"/>
              </security>
            </binding>
            <binding name="wsHttpMessageSecurityBinding">
              <security mode="Message">
                <message clientCredentialType="Certificate" negotiateServiceCredential="false"/>
              </security>
            </binding>
          </wsHttpBinding>
        </bindings>
        <behaviors>
          <endpointBehaviors>
            <behavior name="ClientCertificateBehavior">
              <clientCredentials>
                <!--<clientCertificate findValue="af5445d2126f631d31e310a20cc87079816bf2d4" storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint"/>-->
                <serviceCertificate>
                  <defaultCertificate storeName="My" x509FindType="FindByThumbprint" findValue="a0c44c3df5c642a00820e7fb229e13cff37245f0" storeLocation="LocalMachine"/>
                </serviceCertificate>
              </clientCredentials>
    
            </behavior>
          </endpointBehaviors>
        </behaviors>
        <serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
      </system.serviceModel>
      <system.diagnostics>
        <sources>
          <source name="System.ServiceModel"
                  switchValue="All"
                  propagateActivity="true">
            <listeners>
              <add name="xml" />
            </listeners>
          </source>
          <source name="CardSpace">
            <listeners>
              <add name="xml" />
            </listeners>
          </source>
          <source name="System.IO.Log">
            <listeners>
              <add name="xml" />
            </listeners>
          </source>
          <source name="System.Runtime.Serialization">
            <listeners>
              <add name="xml" />
            </listeners>
          </source>
          <source name="System.IdentityModel">
            <listeners>
              <add name="xml" />
            </listeners>
          </source>
        </sources>
    
        <sharedListeners>
          <add name="xml"
               type="System.Diagnostics.XmlWriterTraceListener"
               initializeData="Traces.svclog" />
        </sharedListeners>
      </system.diagnostics>
    <startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>

    *** Client app.config ***

    *** Service web.config ***

    <configuration>
      <system.serviceModel>
        <services>
          <!-- Logging Service -->
          <service name="Test2WaySSLService.Service1" behaviorConfiguration="messageBehavior">
    
            <endpoint address="" binding="basicHttpBinding" bindingConfiguration="basicTransportBinding" contract="Test2WaySSLService.IService1"/>
            <endpoint name="wsTransportSecurityBinding" address="wsTransportSecurityBinding" binding="wsHttpBinding" bindingConfiguration="wsTransportSecurityBinding" contract="Test2WaySSLService.IService1"/>
            <endpoint name="wsHttpMessageSecurityBinding" address="wsHttpMessageSecurityBinding" binding="wsHttpBinding" bindingConfiguration="wsHttpMessageSecurityBinding" contract="Test2WaySSLService.IService1"/>
          </service>
        </services>
        <bindings>
          <basicHttpBinding>
            <binding name="basicTransportBinding">
              <security mode="Transport">
                <transport clientCredentialType="Certificate"/>
              </security>
            </binding>
          </basicHttpBinding>
          <wsHttpBinding>
            <binding name="wsTransportSecurityBinding">
              <security mode="Transport">
                <transport clientCredentialType="Certificate"/>
              </security>
            </binding>
            <binding name="wsHttpMessageSecurityBinding">
              <security mode="Message">
                <message clientCredentialType="Certificate"  negotiateServiceCredential="false"/>
              </security>
            </binding>
          </wsHttpBinding>
        </bindings>
        <behaviors>
          <serviceBehaviors>
            <behavior name="transportBehavior">
              <!-- To avoid disclosing metadata information, set the value below to false before deployment -->
              <serviceMetadata httpsGetEnabled="true"/>
              <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
              <serviceDebug includeExceptionDetailInFaults="false"/>
            </behavior>
            <behavior name="messageBehavior">
              <!-- To avoid disclosing metadata information, set the value below to false before deployment -->
              <serviceMetadata httpsGetEnabled="true"/>
              <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
              <serviceDebug includeExceptionDetailInFaults="false"/>
              <serviceCredentials>
                <clientCertificate>
                  <authentication certificateValidationMode="None"/>
                </clientCertificate>
                <serviceCertificate storeLocation="LocalMachine" storeName="My" findValue="a0 c4 4c 3d f5 c6 42 a0 08 20 e7 fb 22 9e 13 cf f3 72 45 f0" x509FindType="FindByThumbprint"/>
              </serviceCredentials>
            </behavior>
          </serviceBehaviors>
        </behaviors>
        <serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
      </system.serviceModel>
      <system.web>
        <compilation targetFramework="4.0"/>
        <pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID"/>
      </system.web>
      <system.diagnostics>
        <sources>
          <source name="System.ServiceModel"
                  switchValue="Information, ActivityTracing"
                  propagateActivity="true">
            <listeners>
              <add name="xml" />
            </listeners>
          </source>
          <source name="CardSpace">
            <listeners>
              <add name="xml" />
            </listeners>
          </source>
          <source name="System.IO.Log">
            <listeners>
              <add name="xml" />
            </listeners>
          </source>
          <source name="System.Runtime.Serialization">
            <listeners>
              <add name="xml" />
            </listeners>
          </source>
          <source name="System.ServiceModel.Activation">
            <listeners>
              <add name="xml" />
            </listeners>
          </source>      
          <source name="System.IdentityModel">
            <listeners>
              <add name="xml" />
            </listeners>
          </source>
        </sources>
    
        <sharedListeners>
          <add name="xml"
               type="System.Diagnostics.XmlWriterTraceListener"
               initializeData="Traces.svclog" />
        </sharedListeners>
      </system.diagnostics>
    
    </configuration>

    *** Service web.config ***

    *** Client Code ***

            static void Main(string[] args)
            {
    
                String password = "password";
                //String certificatePath = @"D:\WorkStuff\Test2WaySSLService\ClientTest2WaySSLService\Certificates\Certificate_genericsigning.pfx";
                String certificatePath = @"D:\Documents\Visual Studio 2012\Projects\Test2WaySSLService\ClientTest2WaySSLService\Certificates\Certificate_genericsigning.pfx";
                //ChannelFactory<IService1> cf = new ChannelFactory<IService1>("wsHttpMessageSecurityBinding");
                //ChannelFactory<IService1> cf = new ChannelFactory<IService1>("wsTransportSecurityBinding");
                ChannelFactory<IService1> cf = new ChannelFactory<IService1>("basicTransportBinding");
                try
                {
                    SetCertificateCredentials(cf.Credentials, password, certificatePath);
                
                IService1 c = cf.CreateChannel();
               
                Console.WriteLine(String.Format("GetData(3) = {0}.  Press Return.",c.GetData(3)));
                Console.ReadLine();
                }
                finally
                {
                 cf.Close();
                }
            }
            private static void SetCertificateCredentials(ClientCredentials credentials,String password,String certificatePath)
            {
                credentials.ClientCertificate.Certificate = LoadEmbeddedCertificate(null, null, password, certificatePath);
            }
            static X509Certificate2 LoadEmbeddedCertificate(String resourceType, String embeddedResourceName, String certificatePassword, String certificateFilePath)
            {
                Byte[] certBuffer = null;
                if (!String.IsNullOrEmpty(certificateFilePath))
                {
                    certBuffer = File.ReadAllBytes(certificateFilePath);
                }
    
                //return the certificate 
                X509Certificate2 certificate = new X509Certificate2(certBuffer, certificatePassword);
                return certificate;
            }        
        }

    *** Client Code ***

    *** Service Code ***

        public class Service1 : IService1
        {
            public string GetData(int value)
            {
                return string.Format("You entered: {0}", value);
            }
    
            public CompositeType GetDataUsingDataContract(CompositeType composite)
            {
                if (composite == null)
                {
                    throw new ArgumentNullException("composite");
                }
                if (composite.BoolValue)
                {
                    composite.StringValue += "Suffix";
                }
                return composite;
            }
        }

    *** Service Code ***


    bmukes

    Thursday, August 8, 2013 3:18 PM
  • Here is the Client Traces.svclog file that I added to my drop box folder.  No Traces.svclog was created for the service.

    Traces.svclog


    bmukes

    Thursday, August 8, 2013 3:22 PM
  • simply. first eliminate both transport-related bindings. Make things work for the pure messaging case 0 thereby eliminating issues of IIS config.

    Only have 1 (ws) binding in the client's config file.

    Ensure the default endpoint at the server exposes the messaging-cert binding. Eliminate the other endpoints, for now.

    Thursday, August 8, 2013 8:11 PM
  • I assume you mean eliminate the endpoints rather than the bindings.

    Regardless a WCF web service can listen on multiple endpoints using the same contract as long as the relative addresses are different.  If you examine the web.config you will see that each has a different relative address.  However let me clarify why there are three different bindings.

    1.  I included the endpoint that uses message security so that folks could test that my certificates work correctly.  Feel free to comment it out that endpoint during your testing.

    2.  The endpoint using the basichttpbinding and the endpoint with wshttpbinding both have different relative addresses but again feel free to comment out either binding for your testing purpose.

    Regardless even with only one endpoint present that uses transport security I cannot get certificate authentication with transport security (commonly called 2 way ssl) to work.


    bmukes

    Thursday, August 8, 2013 8:54 PM
  • Noone is going to run your code.

    If you can tell folks that messaging case works/not works with certs, then one can move onto transport.

    Use  standard diagnostic and elimination process, so one fights only one fire at once.

    This is a standard certification test (configure mutual authn binding). One tests the student for correct config, and then diagnostic skills to eliminate. Yes, the transport case is harder, since more wheels turn, expanding the number of causes of error. IN particular, https/SSL comes to bear (that doesnt in a pure messaging/certs binding).

    Thursday, August 8, 2013 9:39 PM
  • OK somehow someone marked this as an answer and its not even close.

    I don't want to start a flame regarding this issue so I will make it rather simple.

    This is the link to the MSDN Patterns and Pratices article How to: Use Certificate Authentication and Transport Security in WCF Calling from Windows Forms that I based my code on.

    I am asking if anyone can get the code in this article working under WCF under the 4.0 or 4.5 framework.  Thats all I need.

    The code I supplied is an enhancement from the article and was designed to confirm that my certificates are valid.

    Yes I have turned on WCF tracing.

    Yes I have confirmed that my certificates are valid.

    Yes I have confirmed that the virtual directory is properly configured.

    And I have found NOTHING in the traces that leads me to a solution to this problem.

    As a matter of fact after searching this site for over 8 hours across 2 weeks I have not found anyone that has gotten this to work.

    The issue is way beyond a simple reply so unless you are willing to try and configure this for yourself (using either my code or the MSDN article please do not bother replying.


    bmukes



    Wednesday, August 14, 2013 3:44 PM
  • run the sample on dot3.5, and turn off the wshttp binding on mex, turning on httpsmex binding. See if you can at least see the metadata using svcutil from a throwaway project. Perhaps add a simple ssl-enabled website, to ensure the core channel is working.

    Lots of things can be wrong. What app pool is IIS runnin the process under? Does the identity have access to the private key? has group policy changed ssl ciphersuites? Who knows.

    Typically, one runs small side-trials to confirm and eliminate all the usual deployment gotchas. Otherwise, one will spend hours in frustration. We have all been there (and learned what to do to get confidence that all the element of the environment are supporting the code).

    why not send the server-side  trace, starting with the message flows? one can normally see in the tracing tool from wcf logs the client and server sides of the pipeline.

    Wednesday, August 14, 2013 5:16 PM
  • Again I do not want this to become a flame but I do not want your reply to be marked as an answer again so I am forced to reply.

    I have tried this under .Net 3.5 it does not work.

    App pool is running as Network Services and Network Services has permission to the private key of the certificate.

    There is NO server side trace.  Typically when you are using Transport security IIS rather than WCF handles security, so if there is a security issue I would NOT expect there to be a WCF trace and there is no WCF trace on the server side.

    I have also made sure that I do not have issues with KB977377.

    Again if you search the forums you will find that I am NOT the only one having this issue and in all of those posts NO one has proposed a solution.

    And finally this is NOT my first rodeo with WCF I know exactly what to do and have done all that over the last 3 weeks.  Soo unless you can confirm that you can get How to: Use Certificate Authentication and Transport Security in WCF Calling from Windows Forms working your replys are not really providing me any information.


    bmukes

    Wednesday, August 14, 2013 7:27 PM
  • Ok the problem partially solved.

    The problem is in the signing certificate I was using on the client side.

    Its a valid certificate for WIF, SSL and wsHttpBinding but it does not pass the mustard for 2 Way SSL via  IIS.

    I replaced my client side certificate with a valid VeriSign signing certificate and finally got everything to work.  So once I analyse the contents of the certificate I should be able to make my own signing certificate using OpenSSL.

    Once that process is complete I will post what properties are required within a certificate to make it work for with 2 Way SSL.

    In case eveyone is wondering why OpenSSL its because we create and store all  certificates in a database so that they can be revoked, downloaded or recreated for testing purposes.


    bmukes


    Wednesday, August 14, 2013 9:23 PM
  • yes, if you use the enterprise CA to issue certs, using the template for clients, it populates the EKUs appropriately for the win32-like enforcement rules that govern transport SSL (by default).

    Thursday, August 15, 2013 9:57 PM