none
Securing a WCF service using a certificate.

    Question

  • Hi, I have a C# WCF service called ProcedureService.svc
    I now wish to secure this service. 
    I started by creating a certificate and added this to the service's web.config file:

    <behavior name="WCF_Procedureservice.ProcedureServiceBehavior">
      <serviceMetadata httpsGetEnabled="true" />
      <serviceAuthorization  principalPermissionMode="UseAspNetRoles" roleProviderName ="SqlRoleManager" />
      <serviceCredentials>
       <userNameAuthentication 
             userNamePasswordValidationMode="MembershipProvider"  
             includeWindowsGroups="false"  
             membershipProviderName="SqlMembershipProvider" />
       <serviceCertificate 
             storeLocation="LocalMachine" 
             storeName="My" 
             x509FindType="FindBySubjectName"  
             findValue="ProcedureService" />
      </serviceCredentials>
      <serviceDebug includeExceptionDetailInFaults="false" />
     </behavior>

    After this i created a C# client and added ProcedureService as a serviceReference.
    It downloaded the certificate and was added successfully.
    I have added the certificate to the client's web config file:

    <behaviors>
          <endpointBehaviors>
            <behavior name="ClientBehavior">
              <clientCredentials>
                <serviceCertificate>
                  <defaultCertificate storeLocation="LocalMachine" storeName="My"
                     x509FindType="FindBySubjectName" findValue="ProcedureService"/>
                  <authentication certificateValidationMode="PeerOrChainTrust" />
                </serviceCertificate>
              </clientCredentials>
            </behavior>
         </endpointBehaviors>
    </behaviors>

    I then attempt to use one of the services web methods and bind the data to a datagrid:

    namespace ProcedureServiceConsumer
    {
        public partial class _Default : System.Web.UI.Page
        {
            protected void Page_Load(object sender, EventArgs e)
            {
                ServiceReference1.ProcedureServiceClient pService = new ServiceReference1.ProcedureServiceClient();
                pService.ClientCredentials.UserName.UserName = "test";
                pService.ClientCredentials.UserName.Password = "test";
               
                System.Data.DataSet dataSet = pService.getAddressTypes();
                gridDataSet.DataSource = dataSet;
                gridDataSet.DataBind();

            }
        }
    }

    This however fails when i try to test the page.
    Right clicking the file in Visual Studio and pressing "view in browser" gives me the following error message:
    (it gave me a norwegian error msg so i had to translate)
    "Could not find X.509 certificate with the following search criteria: StoreName My, StoreLocation LocalMachine, FindType FindBySubjectName, FindValue ProcedureService."


    Does anyone have a clue as to what i am doing wrong here?

    Monday, January 05, 2009 2:55 PM

Answers

  • Yes you do - but it specifies the server cert not the client cert look at the element name under the clientCredentials element

    Regards

    Richard Blewett
    http://www.dotnetconsult.co.uk/weblog2
    • Marked as answer by edhickey Wednesday, February 04, 2009 12:20 AM
    Tuesday, January 06, 2009 1:50 PM

All replies

  • How did you create the certificate?

    Regards

    Richard Blewett
    http://www.dotnetconsult.co.uk/weblog2
    Monday, January 05, 2009 3:18 PM
  • To generate certs I've used: SelfSSL.exe
    available as part of: http://support.microsoft.com/kb/840671

    For testing in development environment i generated a cert with the following command:

    SelfSSL.exe /T /N:CN=localhost /V:999 /Q

    This cert will be placed in the correct store and installed in IIS.

    Using the cert name "localhost" will help avoid a number of issues when developing with a machine that acts as both client and server, if that's what you're doing?

    HTH
    Monday, January 05, 2009 5:03 PM
  • Richard Blewett said:

    How did you create the certificate?

    Regards

    Richard Blewett
    http://www.dotnetconsult.co.uk/weblog2



    Hi Richard,

    I used an application called keyman.
    I followed the instructions in this tutorial:
    http://www.xenocafe.com/tutorials/self_signed_cert_IIS/self_signed_cert_IIS-part1.php

    Hi tanneroni

    I read an msdn doc that suggested using svcutil to generate the cert on client side using the server's wsdl link. I'm attempting that now. perhaps this is my problem? 
    Tuesday, January 06, 2009 8:11 AM
  • Open up the certificate manager and check that the certificate is in the store you expect

    start MMC.exe and add the certificate manager snap-in (select local machine when asked) and then look in the personal store

    Regards

    Richard Blewett
    http://www.dotnetconsult.co.uk/weblog2
    Tuesday, January 06, 2009 8:24 AM
  • In the solution I'm working on (windows form client, IIS hosted services & message security), I add "Service References" to the client project, using the address of the services' mex endpoint defined in the web.config on the server. This should create the binding for the client and place the correct information in your client config.

    Some good sources for WCF info:
    http://www.codeplex.com/WCFSecurityGuide

    http://www.codeplex.com/WCFSecurity/Wiki/View.aspx?title=Questions%20and%20Answers&referringTitle=Home

    Tuesday, January 06, 2009 9:20 AM
  • Hi again.

    Ok i tried this now. The certificate is installed there under personal store.
    As a side-question, how does the save function work in the mmc console? it asks me to save a msc file, which i have done. Although when i reopen the mmc console my certificates do not appear until i actively open the saved msc file.

    ok, so i tried the view in browser option again in visual studio but still have the same error message.
    The error is somewhere i believe in the following line

    <defaultCertificate 
        storeLocation="LocalMachine" 
        storeName="My" 
        x509FindType="FindBySubjectName" 
        findValue="ProcedureService"/>

    i think this is the case because the error message reads:
    "Could not find X.509 certificate with the following search criteria: StoreName My, StoreLocation LocalMachine, FindType FindBySubjectName, FindValue ProcedureService."

    Do i have any of these parameters wrong? the findValue attribute.... what should the value be there? the same name as the filename of the certificate?
    What about the other arguments?

    Thanks for all your help!

    -Thomas
    Tuesday, January 06, 2009 9:26 AM
  • You could trying downloading the WCF samples form the following location:

    http://www.microsoft.com/downloads/details.aspx?FamilyId=2611A6FF-FD2D-4F5B-A672-C002F1C09CCD&displaylang=en

    It contains a working sample for UserNamePasswordValidator under TechnologySamples\Extensibility\Security\UserNamePasswordValidator directory, it shows how to configure the certificates.

    Thanks

    Another Paradigm Shift
    http://shevaspace.blogspot.com
    Tuesday, January 06, 2009 9:41 AM
  • Hi Thomas

    the FindValue should exactly match the common name of the cert

    oh just noticed that the error part of the config is in the client. Are teh service and client running on the same machine? Does the service start OK?

    Regards

    Richard Blewett
    http://www.dotnetconsult.co.uk/weblog2
    Tuesday, January 06, 2009 10:30 AM
  • Hi Richard.

    No, the Service is running on an IIS and the client is running from my laptop. The service is set up as a web site on the IIS and works just fine. I am able to run the web methods just fine until i turn on the SSL.
    You mention common name... should this be generated for the cert or do i have to add a name in mmc?

    Hi Marco.

    Thanks for the link, ill download it and see what it gives me. The biggest problem i have found while working on this matter is that the documentation is either too large or mainly snippets of code without the necessary details for someone starting out. (such as file names and where to put things). I see that the example is 22MB... but it might still prove valuable.

    Tuesday, January 06, 2009 11:13 AM
  • The common name is part of the certificate.

    Is the certificate installed on your laptop as well as the on the server?

    Regards

    Richard Blewett
    http://www.dotnetconsult.co.uk/weblog2
    Tuesday, January 06, 2009 11:16 AM
  • Yes the certificate is installed on both server and client machine :)'
    I tried something new, and it seems that this is getting me closer. i changed the clients web.config to:

    x509FindType="FindByIssuerName" findValue="TD"

    Appartently i am now able to connect to the service. however, when i attempt to call one of the web methods i get the following error:

    ClientCertificate does not exist. please provide a clientcertificate in ClientCredentials.

    mmm...

    Tuesday, January 06, 2009 11:24 AM
  • Is this error on the client or the service?

    What is sthe security setting on the binding?

    Regards

    Richard Blewett
    http://www.dotnetconsult.co.uk/weblog2
    Tuesday, January 06, 2009 11:29 AM
  • The error is on the client.
    So far all the errors i have experienced are when i attempt to connect the client to the service.

    The security setting on the client's binding is:

    <binding name="WSHttpBinding_IProcedureService" closeTimeout="00:01:00" openTimeout="00:01:00"     
                receiveTimeout
    ="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false" transactionFlow="false" 
                hostNameComparisonMode
    ="StrongWildcard" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" 
                messageEncoding
    ="Text" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
        <
    readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" 
                         maxNameTableCharCount
    ="16384" />
        <
    reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />

        <
    security mode="Transport">
            <
    transport clientCredentialType="Certificate" proxyCredentialType="None" realm="" />
            <
    message clientCredentialType="UserName" negotiateServiceCredential="true" establishSecurityContext="true" />
        </
    security>
    </binding>


     

    Tuesday, January 06, 2009 12:00 PM
  • You are telling the client to authenticate using a certificate but you do not tell the client where to find it's certificate. You need an entry someting like this as an endpoint behavior in the client config

    <behaviors>
      <
    endpointBehaviors
    >
        <
    behavior name="auth"
    >
          <
    clientCredentials
    >
            <
    clientCertificate findValue="<cert common name>"

                                       storeLocation="LocalMachine"

                                      
    storeName="My"

                                      
    x509FindType="FindBySubjectName"
    />
          </
    clientCredentials
    >
        </
    behavior
    >
      </
    endpointBehaviors
    >
    </
    behaviors
    >

    Regards

    Richard Blewett
    http://www.dotnetconsult.co.uk/weblog2

    Tuesday, January 06, 2009 1:06 PM
  • Hi richard.

    If you look at my first post you will see that i do have an endpoint behaviour in the client config.

    Perhaps my common name is wrong... ?

     

    Tuesday, January 06, 2009 1:45 PM
  • Yes you do - but it specifies the server cert not the client cert look at the element name under the clientCredentials element

    Regards

    Richard Blewett
    http://www.dotnetconsult.co.uk/weblog2
    • Marked as answer by edhickey Wednesday, February 04, 2009 12:20 AM
    Tuesday, January 06, 2009 1:50 PM