2

I have built a WCF helloworld client and server. I want to use certificate authentication between them.

The error I get is "The caller was not authenticated by the service."

I have created two certificates using makecert.exe. The certificate on the client is installed under 'Personal' and 'Trusted People' and 'Third-Party Root Certification Authorities'. I copied the certificate as I don't know if it should be only under one heading

My server webconfig is as below

            <?xml version="1.0"?>
            <configuration>
              <appSettings>
                <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/>
              </appSettings>
              <system.web>
                <compilation debug="false" targetFramework="4.5.1"/>
                <httpRuntime targetFramework="4.5.1"/>
              </system.web>
              <system.serviceModel>
                <services>
                  <service behaviorConfiguration="BusinessToBusiness" name="TestHelloWork.Service1">

                    <endpoint address="" binding="wsHttpBinding"  bindingConfiguration="BindingConfig" contract="TestHelloWork.IService1" /> 
                            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
                    <host>
                      <baseAddresses>
                        <add baseAddress="http://win-gat-web01:7777/Service1"/>
                      </baseAddresses>
                    </host>
                  </service>
                </services>
                <bindings>  
                  <wsHttpBinding>  
                    <binding name="BindingConfig">
                     <security>
                        <message clientCredentialType = "Certificate"/>
                     </security>
                    </binding>  
                  </wsHttpBinding>  
               </bindings>  
                <behaviors>
                  <endpointBehaviors>
                    <behavior name="webBehavior">
                      <webHttp/>
                    </behavior>
                  </endpointBehaviors>
                  <serviceBehaviors>
                    <behavior>
                      <!-- To avoid disclosing metadata information, set the values below to false before deployment -->
                      <serviceMetadata httpGetEnabled="true" 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="true"/>
                    </behavior>
                    <behavior name="BusinessToBusiness">
                     <serviceCredentials>
                        <clientCertificate>
                           <authentication certificateValidationMode = "PeerTrust"/>
                        </clientCertificate>
                        <serviceCertificate findValue="WCfServer"
                storeLocation="LocalMachine"
                storeName="My"
                x509FindType="FindBySubjectName" />         
                     </serviceCredentials>
                     <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
                    </behavior>     
                  </serviceBehaviors>
                </behaviors>
                <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
              </system.serviceModel>
            </configuration>

The client side web.config is as below

            <?xml version="1.0"?>

            <!--
              For more information on how to configure your ASP.NET application, please visit
              http://go.microsoft.com/fwlink/?LinkId=169433
              -->

            <configuration>

                <system.web>
                  <compilation debug="true" targetFramework="4.5.1" />
                  <httpRuntime targetFramework="4.5.1" />
                </system.web>
              <system.serviceModel>
            <bindings>
              <wsHttpBinding>
                <binding name="WSHttpBinding_IService1">
                  <security>
                    <message clientCredentialType="Certificate" />

                  </security>
                </binding>
              </wsHttpBinding>
            </bindings>
            <client>
              <endpoint address="http://myserver:7777/Service.svc" binding="wsHttpBinding"
                bindingConfiguration="WSHttpBinding_IService1" contract="ServiceReference1.IService1"
                name="WSHttpBinding_IService1" behaviorConfiguration="CustomBehavior">
                <identity>
                  <dns value="WCfServer" />
                </identity>
              </endpoint>
            </client>
            <behaviors>
              <endpointBehaviors>
                <behavior name="CustomBehavior">
                  <clientCredentials>
                    <clientCertificate findValue="WcfClient" x509FindType="FindBySubjectName"

                      storeLocation="CurrentUser" storeName="My" />
                    <serviceCertificate>
                      <authentication certificateValidationMode="PeerTrust"/>
                    </serviceCertificate>
                  </clientCredentials>
                </behavior>
              </endpointBehaviors>
            </behaviors>
            </system.serviceModel>
            </configuration>

The client side certificate is installed under 'Personal'

Any idea what could be wrong? I have googled and realised that they should be on same domain? But they are. Also how will the domain work when my service is external?

Aryan Firouzian
  • 1,940
  • 5
  • 27
  • 41
user2837961
  • 1,505
  • 3
  • 27
  • 67

2 Answers2

4

Try to enable CAPI2 log. It is special log (not enabled by default) that contains certificate validation information. If your problem lies is in certificate validation procedure failing you will find useful info there. Look for errors. In my case it was something like

  • A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider.
  • A certificate chain could not be built to a trusted root authority.
  • revocation function was unable to check revocation ...

CAPI2 log

In my case I've had

  • Client.pfx imported to CurrentUser\My (personal) store on client.
  • CA certificate (ClientCA) that issued client.pfx was imported to Trusted root authorities at both LocalMachine store on client and LocalMachine store on server.
  • server.pfx imported in LocalMachine\My (personal) store on server
  • CA certificate (ServerCA) that issued server.pfx imported to Trusted root authorities at both LocalMachine store on client and LocalMachine store on server.
  • CRL to Client CA available from both client and server
  • CRL to Server CA available from both client and server

But you are using PeerTrust certificate validation mode so according to this comment from WCF demos I have

Setting the certificateValidationMode to PeerOrChainTrust means that if the certificate is in the user's Trusted People store, then it will be trusted without performing a validation of the certificate's issuer chain. This setting is used here for convenience so that the sample can be run without having to have certificates issued by a certificate authority (CA).

I would assume that certificates should be placed like this:

  • client.cer (only certificate from client.pfx) imported to Trusted people store in LocalMachine store on server
  • server.cer (only certificate from client.pfx) imported to Trusted people store in CurrentUser store on client

Verify that you have granted rights on server private key to AppPool that your IIS WCF service runs under (default pool is IIS APPPOOL\DefaultAppPool) It can be done using mmc or certlm.msc by right clilcking on server certificate then All Tasks -> Manage Private Keys .... Verify that you do NOT have selected AD because IIS APPPOOL is a local group. Add account IIS APPPOOL\your_pool_name and hit OK.

That will work if you have default settings in your AppPool like Identity is set to AplicationPoolIdentity and not a custom account (it is common to use managed service account from AD) and Load User Profile is set to true.

Add rights to server private key Default app pool settings

pepo
  • 8,644
  • 2
  • 27
  • 42
  • Hi, I get "The certificate is not valid for the requested usage" in the log? Any ideas? – user2837961 Nov 20 '17 at 13:21
  • 1
    Server certificate should contain EnhancedKeyUsage extension `Server Authentication (1.3.6.1.5.5.7.3.1)` and client certificate should contain `Client Authentication (1.3.6.1.5.5.7.3.2)` extension. – pepo Nov 20 '17 at 13:43
  • That has not made any difference – user2837961 Nov 20 '17 at 14:29
  • The error in the log is "The signature of the certificate cannot be verified" – user2837961 Nov 20 '17 at 14:53
  • @user2837961 OK, so generate certificates that can be verified. I often use [XCA](https://sourceforge.net/projects/xca/) to do that. It has already predefined templates for server certificate and client certificate and also CA template so you can simulate production environment. – pepo Nov 20 '17 at 15:05
  • Using xca. How to I resolve "It is likely that certificate 'CN=MySub' may not have a private key that is capable of key exchange or the process may not have access rights for the private key. Please see inner exception for detail" – user2837961 Nov 20 '17 at 15:52
  • If you're hosting it on IIS then you have to give rights to app pool on private key of server certificate. – pepo Nov 20 '17 at 18:02
  • Yes I am using IIS. I tried giving rights by right clicking -> manage private keys, but don't get the option? – user2837961 Nov 21 '17 at 08:46
  • @user2837961 I've updated my answer with info about granting rights to private key – pepo Nov 21 '17 at 09:38
  • Yes I know it should be there but the menu is not there. I have added the console using Local Account – user2837961 Nov 21 '17 at 09:39
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/159462/discussion-between-pepo-and-user2837961). – pepo Nov 21 '17 at 09:49
0

The WCF certificate authentication issue you are encountering is most likely related to the options used when generating your self-signed certificates with MakeCert.

In particular, make sure your certificate supports the necessary options/purposes. (e.g. Intended Purposes field of the certificate should include an appropriate value, such as "Server Authentication" or "Client Authentication".)

The following link from Microsoft details the process.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-create-temporary-certificates-for-use-during-development

Note: our team has had good luck using the SelfCert tool for quickly generating development, self-signed certificates.

Seymour
  • 7,043
  • 12
  • 44
  • 51