Tuesday, November 5, 2013

wcf message security using certificates

=======================================================
WCF MESSAGE SECURITY USING CERTIFICATE PART 1 : SERVICE
=======================================================


1. Create a new Web Site.

2. Add a WCF web service to the site.

3. Change the DoWork function: (in both IService.cs and Service.cs)
string DoWork(int a)
{
return "You entered " + a.ToString();
}
4. In principle, following things are required to enable certificate
security at service level :
a. Define the security level : either transport or message
  This is defined in the binding configuration
b. Provide the certificate :
  This is done in ServiceBehaviors - ServiceCredentials tag.
c. Attach the above to Service.
  Attach message security by specifying bindingConfiguration in
  endpoint.
  Attach service certificate by specifying behaviorConfiguration
  in service.

4a.
<bindings>
      <wsHttpBinding>
        <binding name="mybind" >
          <security mode="Message">
            <message clientCredentialType="Certificate"/>
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>

4b.
<serviceBehaviors>
        <behavior name="my">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
          <serviceCredentials>
            <serviceCertificate findValue="CN=tempCert" storeLocation="LocalMachine" storeName="My"/>
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>


5. Mex endpoint : this is extra; but may be required.
a. In service behavior :
    <serviceMetadata httpGetEnabled="true" />
b. In <service> tag, add another endpoint for mex:
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" ></endpoint>

6. The total config should now look like following :
 <system.serviceModel>
    <bindings>
      <wsHttpBinding>
        <binding name="mybind" >
          <security mode="Message">
            <message clientCredentialType="Certificate"/>
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="my">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
          <serviceCredentials>
            <serviceCertificate findValue="CN=tempCert" storeLocation="LocalMachine" storeName="My"/>
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
   
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
    <services>
      <service name="Service" behaviorConfiguration="my" >
        <endpoint address="" binding="wsHttpBinding" contract="IService" bindingConfiguration="mybind"></endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" ></endpoint>
      </service>
    </services>
  </system.serviceModel>

7. Run the web site with Service.svc open. You should see familiar blue/white WSDL screen for the service.
   The url of the page looks something like "http://localhost:4348/WebSite1/Service.svc"

======================================================
WCF MESSAGE SECURITY USING CERTIFICATE PART 2 : CLIENT
======================================================

1. Create a new web site.

2. Add a Label Label1 on default.aspx.

3. Add a service reference to the project.
Right click the project in solution explorer, select "Add Service Reference".
Copy and paste the url of the service from the page displayed in step 7 above.
        Click "Go" in the "Add Service Reference" dialog.
The service should be seen in "Services" pane of the dialog.
Click "OK" to add the reference.
4. Add the following code to Page_Load event of Default.aspx :
        ServiceReference1.ServiceClient svc = new ServiceReference1.ServiceClient();
        Label1.Text = svc.DoWork(4);

5. Run the project. You should get "InvalidOperationException" exception:
"The client certificate is not provided. Specify a client certificate in ClientCredentials."

6. To correct this error, you need to provide a certificate to the client.
   This done in the endpoint behavior.
   So you need to create an endpoint behavior and attach it to endpoint.

7.Here we will the same certificate as that of service, since we are trying this on the same machine.
Add the following to the <system.serviceModel> section:
   <behaviors>
      <endpointBehaviors>
        <behavior name="myb">
          <clientCredentials>
            <clientCertificate findValue="CN=tempCert" storeLocation="LocalMachine" storeName="My"/>
          </clientCredentials>
        </behavior>
      </endpointBehaviors>
    </behaviors>

8. Attach this behavior to the endpoint specified in <client> tag.
The total configuration looks like following :

  <system.serviceModel>
    <behaviors>
      <endpointBehaviors>
        <behavior name="myb">
          <clientCredentials>
            <clientCertificate findValue="CN=tempCert" storeLocation="LocalMachine" storeName="My"/>
          </clientCredentials>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <bindings>
      <wsHttpBinding>
        <binding name="WSHttpBinding_IService" 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="Message">
            <transport clientCredentialType="Windows" proxyCredentialType="None" realm=""/>
            <message clientCredentialType="Certificate" negotiateServiceCredential="true" algorithmSuite="Default"/>
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <client>
      <endpoint address="http://localhost:4348/WebSite1/Service.svc" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService" contract="ServiceReference1.IService" name="WSHttpBinding_IService" behaviorConfiguration="myb">
        <identity>
          <certificate encodedValue="AwAAAAEAAAAUAAAAJdm3kjyo+3IINpRcCcL18xAHDJMgAAAAAQAAAPMBAAAwggHvMIIBXKADAgECAhAJcTbJKUQFrkBJG29u5jGAMAkGBSsOAwIdBQAwFTETMBEGA1UEAxMKUm9vdENBVGVzdDAeFw0xMzEwMjkxMzI1MTlaFw0zOTEyMzEyMzU5NTlaMBMxETAPBgNVBAMTCHRlbXBDZXJ0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCsThCFbp8o2lRth4OHqTYUZzvh08fnkEn+mlVKiJwe1uBOcPSCummedCI2SOkH/pCFc6VRcJcSv77UL1fI8KcQbJWgNwwlikrjJxJfuzQT8I/3ECWCaE3iZRqHMqE31BNgQR/dp/mRo8U3KSUzkgnmS/pcceDO4TA/ZX3gB399CQIDAQABo0owSDBGBgNVHQEEPzA9gBCKdgxdb66XAzRBYr0zZTZMoRcwFTETMBEGA1UEAxMKUm9vdENBVGVzdIIQp+Iu4taQ8a9NIeWKlHq9YDAJBgUrDgMCHQUAA4GBAAKB8SfWcyTkFeOdfx1YEEfjqYRuwgZBn7eHEhCk6lxFi5OZLtxZfxULR0bSbd6F4QMBDlcHM0LFaf6SKJ6JCXZQ5dfBHzgcJJ5j+Okj59be9DrYIbunut5uMLWJAx+nIFCItxe4hw452U+DAKEOSHI83OANdzzbFgAckiwFheOu"/>
        </identity>
      </endpoint>
    </client>
  </system.serviceModel>


9.Run the project. Now it should run without exception and should show a message like following on default.aspx:
"You enetered 4".




NOTE 1 : In both service and client, you can use "CN=" prefix in certificate, You then need not to specify x509FindType.
specifying "CN=" is equivalent to x509FindType="FindBySubjectName".

NOTE 2: In general the certificate should be valid. To check this, double click on the certicate in MMC.
In the "General" tab, look at the "Certificate Information". This information should not be something like certificate not trusted,
certificate expired, etc.

In one of the cases, a certificate was having this info (status) as
"This CA Root certificate is not trusted because it is not in the Trusted Root Certification Authorities store."

This certificate was placed in LocalMachine TrustedPeople.

The exception received was SecurityNegotiationException :
"The caller was not authenticated by the service."

NOTE 3: Also verify that the certificate is having a private key.
This can be verified on the "General" tab, at the bottom you should see a message like :
"You have a private key that corresponds to this certificate."


No comments:

Post a Comment