.NET Framework Developer Center > .NET Development Forums > Windows Communication Foundation > X509 Certificates between a web service and another service
Ask a questionAsk a question
 

AnswerX509 Certificates between a web service and another service

  • Sunday, December 11, 2005 1:00 PMWilly-Peter Schaub Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Using X509 certificates between a user and a web service seems "failry" stright forward. However, how does one configure the X.509 certificates to work between a web service (running a network service by default) and another WCF service? I have trief everything using tools to the likes of FindPrivateKey and MakeCert ... to no avail.

Answers

  • Thursday, December 22, 2005 10:00 PMJan Alexander Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    You need to configure you WCF service as you configure your WCF client in addition to the configuration that the service already has. The following must be true in order to make certificate authentication work.

    Let's name service that is calling other service A and the service that will be called B.

    1. The certificate service A will be using to call service B must be trusted by service B. That means that this certificate must be either in Trusted People store or must chain up to a certificate that is trusted by service B.
    2. The same must apply for certificate service B will be using to accept request on the service A side.
    3. You need to add client configuration to service A web.config. The configuration that you will be adding is the same you use for regular clients that call service B. You can generate it using svcutil from the service B WSDL. Make sure that you leave the original configuration for service A in place, otherwise service A will no longer be able to serve as a service.

    If you are hosting service A and/or service B in the IIS, you need to store the certificate in the LocalMachine store location not in the CurrentUser. Additionally you need to make sure that both Service A and Service B have access to the private keys associated with their certificates. This is the same as for any other IIS hosted service. The only difference is that service A now can have two certificates and therefore two private keys that it needs access. This is true when you will use different certificates to accept requests and to make calls to service B on service A.

    This is an example of the web.config for the service A. In this example service A uses two certificates.

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <system.serviceModel>

        <services>
          <service
         type="Microsoft.ServiceModel.Samples.CalculatorService"
         behaviorConfiguration="CalculatorServiceBehavior">
            <!-- use base address provided by host -->
            <endpoint address=""
              binding="wsHttpBinding"
              bindingConfiguration="Binding1"
              contract="Microsoft.ServiceModel.Samples.ICalculator" />
          </service>
        </services>

        <client>
          <!-- Use a behavior to configure the client certificate to present to the service. -->
          <endpoint name=""
                    address="
    http://localhost/service1/service1.svc"
                    binding="wsHttpBinding"
                    bindingConfiguration="Binding2"
                    behaviorConfiguration="ClientCertificateBehavior"
                    contract="IService1">
            <!-- Include the identity element When running cross-machine in a domain.
                  The dns value should be name of the serviceCertificate-->
            <!--<identity>
                 <dns value ="localhost"/>
               </identity>-->
          </endpoint>
        </client>

        <bindings>
          <wsHttpBinding>
            <!--
        This configuration defines the security mode as Message and
        the clientCredentialType as Certificate.
        -->
            <binding name="Binding1">
              <security mode ="Message">
                <message clientCredentialType="Certificate" />
              </security>
            </binding>
            <binding name="Binding2">
              <security mode="Message">
                <message clientCredentialType="Certificate"/>
              </security>
            </binding>
          </wsHttpBinding>
        </bindings>

        <behaviors>
          <behavior name="CalculatorServiceBehavior"
                    returnUnknownExceptionsAsFaults="true" >
            <!--
        The serviceCredentials behavior allows one to define a service certificate.
        A service certificate is used by a client to authenticate the service and provide message protection.
        This configuration references the "localhost" certificate installed during the setup instructions.
        -->
            <serviceCredentials>
              <serviceCertificate findValue="localhost" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
            </serviceCredentials>
          </behavior>
          <behavior name="ClientCertificateBehavior">
            <!--
        The clientCredentials behavior allows one to define a certificate to present to a service.
        A certificate is used by a service to authenticate the client and provide message protection.
        This configuration references the "client.com" certificate installed during the setup instructions.
        -->
            <clientCredentials>
              <clientCertificate findValue="client.com" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
            </clientCredentials>
          </behavior>
        </behaviors>
      </system.serviceModel>
      <system.web>
        <compilation debug="true" />
      </system.web>
    </configuration>

    I hope this wil help you to make this working.

    Jan

     

  • Thursday, January 05, 2006 1:57 AMJan Alexander Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    I see two things:

    1. You need to copy the "CalculatorServer" certificate to the trusted people store in store location that belongs to the account the subtract service is running under (for subtract service to trust calculation service certificate). You also need to copy the "CalculatorServices" certificate to the trusted people store in the LocalMachine location (for the calculation service to trust subtract service certificate). You can use CertMgr for this in the same way WCF samples do.
    2. You have typo in the Calculator service configuration (there is a missing "s" character in the service certificate configuration in the client credentials in "ClientCertificateBehavior" behavior):

    <serviceCertificate findValue="CalculatorServices" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />

    There is no difference between web hosted service and console service from the message security perspective except for the user account under which the service code runs. Therefore you need to operate over multiple certificate store locations.

    I hope this will help you make your app working.

    --Jan

  • Thursday, January 05, 2006 7:48 AMWilly-Peter Schaub Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    The trusted people store was my downfall. Thanks for your invaluable assiatance!

All Replies

  • Thursday, December 22, 2005 10:00 PMJan Alexander Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    You need to configure you WCF service as you configure your WCF client in addition to the configuration that the service already has. The following must be true in order to make certificate authentication work.

    Let's name service that is calling other service A and the service that will be called B.

    1. The certificate service A will be using to call service B must be trusted by service B. That means that this certificate must be either in Trusted People store or must chain up to a certificate that is trusted by service B.
    2. The same must apply for certificate service B will be using to accept request on the service A side.
    3. You need to add client configuration to service A web.config. The configuration that you will be adding is the same you use for regular clients that call service B. You can generate it using svcutil from the service B WSDL. Make sure that you leave the original configuration for service A in place, otherwise service A will no longer be able to serve as a service.

    If you are hosting service A and/or service B in the IIS, you need to store the certificate in the LocalMachine store location not in the CurrentUser. Additionally you need to make sure that both Service A and Service B have access to the private keys associated with their certificates. This is the same as for any other IIS hosted service. The only difference is that service A now can have two certificates and therefore two private keys that it needs access. This is true when you will use different certificates to accept requests and to make calls to service B on service A.

    This is an example of the web.config for the service A. In this example service A uses two certificates.

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <system.serviceModel>

        <services>
          <service
         type="Microsoft.ServiceModel.Samples.CalculatorService"
         behaviorConfiguration="CalculatorServiceBehavior">
            <!-- use base address provided by host -->
            <endpoint address=""
              binding="wsHttpBinding"
              bindingConfiguration="Binding1"
              contract="Microsoft.ServiceModel.Samples.ICalculator" />
          </service>
        </services>

        <client>
          <!-- Use a behavior to configure the client certificate to present to the service. -->
          <endpoint name=""
                    address="
    http://localhost/service1/service1.svc"
                    binding="wsHttpBinding"
                    bindingConfiguration="Binding2"
                    behaviorConfiguration="ClientCertificateBehavior"
                    contract="IService1">
            <!-- Include the identity element When running cross-machine in a domain.
                  The dns value should be name of the serviceCertificate-->
            <!--<identity>
                 <dns value ="localhost"/>
               </identity>-->
          </endpoint>
        </client>

        <bindings>
          <wsHttpBinding>
            <!--
        This configuration defines the security mode as Message and
        the clientCredentialType as Certificate.
        -->
            <binding name="Binding1">
              <security mode ="Message">
                <message clientCredentialType="Certificate" />
              </security>
            </binding>
            <binding name="Binding2">
              <security mode="Message">
                <message clientCredentialType="Certificate"/>
              </security>
            </binding>
          </wsHttpBinding>
        </bindings>

        <behaviors>
          <behavior name="CalculatorServiceBehavior"
                    returnUnknownExceptionsAsFaults="true" >
            <!--
        The serviceCredentials behavior allows one to define a service certificate.
        A service certificate is used by a client to authenticate the service and provide message protection.
        This configuration references the "localhost" certificate installed during the setup instructions.
        -->
            <serviceCredentials>
              <serviceCertificate findValue="localhost" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
            </serviceCredentials>
          </behavior>
          <behavior name="ClientCertificateBehavior">
            <!--
        The clientCredentials behavior allows one to define a certificate to present to a service.
        A certificate is used by a service to authenticate the client and provide message protection.
        This configuration references the "client.com" certificate installed during the setup instructions.
        -->
            <clientCredentials>
              <clientCertificate findValue="client.com" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
            </clientCredentials>
          </behavior>
        </behaviors>
      </system.serviceModel>
      <system.web>
        <compilation debug="true" />
      </system.web>
    </configuration>

    I hope this wil help you to make this working.

    Jan

     

  • Saturday, December 24, 2005 10:48 AMWilly-Peter Schaub Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    I have digested and got the WinFX samples to work, I have developed my own equivalent and managed to get it to work. However, as soon as I move over to the web service and system service I am hit with "System.ServiceModel.UnknownFaultException: The request for security token could not be satisfied because authentication failed" and am not able to resolve it ... in fact it is getting worse as I "fiddle" and the certificates are driving me insane.
     
    My problem lies with a demo CalculationService (IIS hosted WCF service) and a Subtract Service (System Service).  Below the web service config file and the subtract service config file, plus the certificate setup batch file I used. Note that the subtract service is a system service, running with the credentials of local administrator, same as the credentials used when running the setup file.
     
    What is different when calling a service from a web service hosted service, to the client->service or console service->console service? Any advice will be much appreciated, because I am slowly loosing it ...
     
    WEB.CONFIG
    ==========
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <system.serviceModel>
        <!-- Service Configuration -->
        <services>
          <service type="Bbd.IndigoCompanionSolution.Calculator,Bbd.IndigoCompanionSolution.CalculationService">
     <!-- behaviorConfiguration="ServiceCertificateBehavior"-->
            <endpoint
              address="
    http://LocalHost/CalculationService/CalculationService.svc"
              binding="wsDualHttpBinding"
              contract="Bbd.IndigoCompanionSolution.ICalculationRequest,Bbd.IndigoCompanionSolution.Contracts"/>
          </service>
        </services>
        <!-- ENDPOINT configuration ============================================================ -->
        <client>
          <endpoint name="WSHttpBinding_ICalculationSubtract"
                    address="
    http://localhost:8090/SubtractService"
                    binding="wsHttpBinding"
                    bindingConfiguration="WSHttpBinding_ICalculationSubtract"
                    behaviorConfiguration="ClientCertificateBehavior"
                    contract="ICalculationSubtract">
                    <identity>
                      <dns value="CalculatorServices" />
                    </identity>
          </endpoint>
        </client>
        <!-- BINDING configuration ============================================================ -->
        <bindings>
       
          <!-- SUBTRACTION Service -->
          <wsHttpBinding>
            <!-- This configuration defines the security mode as Message and
               the clientCredentialType as Certificate. -->
            <binding name="WSHttpBinding_ICalculationSubtract">
              <reliableSession enabled="true"/>
              <security mode="Message" >
                <message clientCredentialType="Certificate" />
              </security>
            </binding>
          </wsHttpBinding>
         
        </bindings>
        <behaviors>
          <behavior name="ServiceCertificateBehavior">
            <!-- The serviceCredentials behavior allows one to define a service certificate.
                 A service certificate is used by a client to authenticate the service and provide message protection.
                 This configuration references the "localhost" certificate installed during the setup instructions.
            -->
            <serviceCredentials>
              <serviceCertificate findValue="CalculatorService" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
            </serviceCredentials>
          </behavior>
          <behavior name="ClientCertificateBehavior">
            <!-- The clientCredentials behavior allows one to define a certificate to present to a service.
               A certificate is used by a service to authenticate the client and provide message protection.
               This configuration references the "client.com" certificate installed during the setup instructions.
          -->
            <clientCredentials>
              <serviceCertificate findValue="CalculatorService" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
              <clientCertificate  findValue="CalculatorServer"  storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
            </clientCredentials>
          </behavior>
        </behaviors>
      </system.serviceModel>
    </configuration>
    SUBTRACT SERVICE CONFIG
    =====================
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <appSettings>
        <!-- use appSetting to configure base address provided by host -->
        <add key="baseAddress" value="
    http://localhost:8090/SubtractService" />
      </appSettings>
      <system.serviceModel>
        <services>
          <service    type="Bbd.IndigoCompanionSolution.SubtractService,Bbd.IndigoCompanionSolution.SubtractService"
                      behaviorConfiguration="SubtractServiceBehavior" >
            <endpoint address="
    http://localhost:8090/SubtractService"
                      binding="wsHttpBinding"
                      bindingConfiguration="BindingSubtract"
                      contract="Bbd.IndigoCompanionSolution.ICalculationSubtract" />
          </service>
        </services>
        <bindings>
          <wsHttpBinding>
            <!--
         This configuration defines the security mode as Message and
         the clientCredentialType as Certificate.
         -->
            <binding name="BindingSubtract">
              <reliableSession enabled="true"/>
              <security mode ="Message">
                <message clientCredentialType="Certificate" />
              </security>
            </binding>
          </wsHttpBinding>
        </bindings>
        <behaviors>
          <behavior
             name="SubtractServiceBehavior"
             returnUnknownExceptionsAsFaults="true" >
            <!--
         The serviceCredentials behavior allows one to define a service certificate.
         A service certificate is used by a client to authenticate the service and provide message protection.
         This configuration references the "localhost" certificate installed during the setup instructions.
         -->
            <serviceCredentials>
              <serviceCertificate findValue="CalculatorServices" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
            </serviceCredentials>
          </behavior>
        </behaviors>
      </system.serviceModel>
      <system.web>
        <compilation debug="true" />
      </system.web>
    </configuration>
     
    SETUP FILE
    ========
    CertMgr.exe -del -r LocalMachine -s My -c -n CalculatorServer
    CertMgr.exe -del -r LocalMachine -s My -c -n CalculatorServices
    makecert -n "CN=CalculatorServer" -pe -sr localmachine -ss My -a sha1 -sky exchange
    makecert -n "CN=CalculatorServices" -pe -sr localmachine -ss My -a sha1 -sky exchange
    for /F "delims=" %%i in ('FindPrivateKey.exe My LocalMachine -n "CN=CalculatorServer" -a') do (cacls.exe "%%i" /E /G "NT AUTHORITY\NETWORK SERVICE":R)
    for /F "delims=" %%i in ('FindPrivateKey.exe My LocalMachine -n "CN=CalculatorServices" -a') do (cacls.exe "%%i" /E /G "NT AUTHORITY\NETWORK SERVICE":R)
    for /F "delims=" %%i in ('FindPrivateKey.exe My LocalMachine -n "CN=CalculatorServer" -a') do (cacls.exe "%%i" /E /G "ASPNET":R)
    for /F "delims=" %%i in ('FindPrivateKey.exe My LocalMachine -n "CN=CalculatorServices" -a') do (cacls.exe "%%i" /E /G "ASPNET":R)
    pause 
  • Thursday, January 05, 2006 1:57 AMJan Alexander Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    I see two things:

    1. You need to copy the "CalculatorServer" certificate to the trusted people store in store location that belongs to the account the subtract service is running under (for subtract service to trust calculation service certificate). You also need to copy the "CalculatorServices" certificate to the trusted people store in the LocalMachine location (for the calculation service to trust subtract service certificate). You can use CertMgr for this in the same way WCF samples do.
    2. You have typo in the Calculator service configuration (there is a missing "s" character in the service certificate configuration in the client credentials in "ClientCertificateBehavior" behavior):

    <serviceCertificate findValue="CalculatorServices" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />

    There is no difference between web hosted service and console service from the message security perspective except for the user account under which the service code runs. Therefore you need to operate over multiple certificate store locations.

    I hope this will help you make your app working.

    --Jan

  • Thursday, January 05, 2006 7:48 AMWilly-Peter Schaub Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    The trusted people store was my downfall. Thanks for your invaluable assiatance!