Thursday, 30 May 2013

Message Security with Certificates for Client/Server validation

few reads

http://msdn.microsoft.com/en-us/library/ms733098.aspx
http://msdn.microsoft.com/en-us/library/ms751516.aspx
http://stackoverflow.com/questions/1570939/wcf-message-security-without-certificate-and-windows-auth

and create two certificates - for client/server.

Server

ICalculator.cs
using System.ServiceModel;
 
namespace MySimpleCalculator
{
    [ServiceContract]
    public interface ICalculator
    {
        [OperationContract]
        int Add(int num1, int num2);
 
        [OperationContract]
        string GetCallerIdentity();
    }
}

Calculator.svc
<%@ ServiceHost Language="C#" Debug="true" Service="MySimpleCalculator.Calculator" CodeBehind="Calculator.svc.cs" %>
Calculator.svc.cs

using System.ServiceModel;
namespace MySimpleCalculator
{
    public class Calculator : ICalculator
    {
        public int Add(int num1, int num2)
        {
            return num1 + num2;
        }
 
        public string GetCallerIdentity()
        {
            // The client certificate is not mapped to a Windows identity by default.
            // ServiceSecurityContext.PrimaryIdentity is populated based on the information
            // in the certificate that the client used to authenticate itself to the service.
            return ServiceSecurityContext.Current.PrimaryIdentity.Name;
        }
    }
}
 
web.config 

<?xml version="1.0"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="ServiceCredentialsBehavior">
          <serviceCredentials>
            <serviceCertificate findValue="CN=Server" 
                 x509FindType="FindBySubjectDistinguishedName"/>
            <clientCertificate>
              <authentication certificateValidationMode="PeerOrChainTrust" 
                    revocationMode="NoCheck" />
            </clientCertificate>
          </serviceCredentials>
          <!-- To avoid disclosing metadata information, set the value below to false 
                and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="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>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service name="MySimpleCalculator.Calculator" behaviorConfiguration="ServiceCredentialsBehavior">
        <endpoint contract="MySimpleCalculator.ICalculator" address="/SimpleCalculator" 
                  binding="wsHttpBinding"
                  bindingConfiguration="MessageUsingCertificate" />
      </service>
    </services>
    <bindings>
      <wsHttpBinding>
        <binding name="MessageUsingCertificate">
          <security mode="Message">
            <message clientCredentialType="Certificate"/>
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
 <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>
  
</configuration>
------------------------------------------------------------------------

client

class Program
{
   static void Main(string[] args)
   {
      ServiceReference1.CalculatorClient client = new ServiceReference1.CalculatorClient();
 
      Console.WriteLine(client.Add(1, 2));
      Console.WriteLine(client.GetCallerIdentity());
 
      Console.ReadLine();
    }
}
 
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <bindings>
            <wsHttpBinding>
                <binding name="WSHttpBinding_ICalculator">
                    <security>
                        <message clientCredentialType="Certificate" />
                    </security>
                </binding>
            </wsHttpBinding>
        </bindings>
      <behaviors>
        <endpointBehaviors>
          <behavior name="endpointCredentialsBehavior">
            <clientCredentials>
              <clientCertificate findValue="CN=client"
                 storeLocation="LocalMachine"
                x509FindType="FindBySubjectDistinguishedName" />
              <serviceCertificate>
                <authentication revocationMode="NoCheck"/>
              </serviceCertificate>
            </clientCredentials>
          </behavior>
        </endpointBehaviors>
      </behaviors>
        <client>
            <endpoint address="http://localhost:18499/Calculator.svc/SimpleCalculator"
                binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_ICalculator"
                contract="ServiceReference1.ICalculator" name="WSHttpBinding_ICalculator"
                behaviorConfiguration="endpointCredentialsBehavior">
                <identity>
                    <certificate encodedValue="server certificate value" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
</configuration> 


Issues

1. {"The request for security token could not be satisfied because authentication failed."} - It means
   Server tries to authenticate Client certificate and its revocation status. So add 'No Revocation'
   @ server side for client certificate.
 
2. SOAP security negotiation with "uri" fails.

   The X.509 certificate CN=subject chain building failed. The certificate that was used has 
   a trust chain that cannot be verified. Replace the certificate or change the 
   certificateValidationMode. The revocation function was unable to check revocation for 
   the certificate. - This happens if server certificate is not trusted by client, add no revocation.
 
     

No comments: