Gettings faults in message security mode

Jun 21, 2009 at 10:59 PM

I could not make ExceptionHandling project work when the service is secured using message security with client & server certificates. In the processReply method in CFClientBaseEx class the statement "if (reply.IsFault)" always returns false eventhough the reply is a Fault message. Here is the service (partial) configuration I used:

 

      <service name="Greetings.Services.GreetingService" behaviorConfiguration="MobileServiceBehavior">
        <endpoint address="GreetingService" binding="basicHttpBinding" bindingConfiguration="SecureMessageMobileBinding" contract="Greetings.Services.IGreetingService" >
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://mobilewcf.com:8000"/>
          </baseAddresses>
        </host>
      </service>
    </services>


<binding name="SecureMessageMobileBinding" > <security mode="Message" > <message algorithmSuite="Basic256Rsa15" clientCredentialType="Certificate" /> </security> </binding>
        <behavior name="MobileServiceBehavior">
          <serviceMetadata httpGetEnabled="True" />
          <serviceDebug includeExceptionDetailInFaults="true" />
          <serviceCredentials>
            <serviceCertificate findValue="mobilewcf.com" storeLocation="LocalMachine" storeName="Root" x509FindType="FindBySubjectName" />
            <clientCertificate>
              <authentication certificateValidationMode="ChainTrust" revocationMode="NoCheck"/>
            </clientCertificate>
          </serviceCredentials>
        </behavior>

 

As for CF client, I changed the service setup part as follows:

      //WCF.Binding binding = GreetingServiceClient.CreateDefaultBinding();
//m_proxy = new GreetingServiceClient(binding, new EndpointAddress(GreetingServiceClient.EndpointAddress.Uri.ToString()));

System.ServiceModel.Channels.CustomBinding binding = new System.ServiceModel.Channels.CustomBinding();
binding.Elements.Add(new System.ServiceModel.Channels.TextMessageEncodingBindingElement(System.ServiceModel.Channels.MessageVersion.Soap11, System.Text.Encoding.UTF8));
System.ServiceModel.Channels.AsymmetricSecurityBindingElement asbe = new System.ServiceModel.Channels.AsymmetricSecurityBindingElement(new System.ServiceModel.Security.Tokens.X509SecurityTokenParameters(System.ServiceModel.Security.Tokens.X509KeyIdentifierClauseType.Any, System.ServiceModel.Security.Tokens.SecurityTokenInclusionMode.Never), new System.ServiceModel.Security.Tokens.X509SecurityTokenParameters(System.ServiceModel.Security.Tokens.X509KeyIdentifierClauseType.Any, System.ServiceModel.Security.Tokens.SecurityTokenInclusionMode.AlwaysToRecipient));
asbe.MessageSecurityVersion = System.ServiceModel.MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10;
asbe.LocalClientSettings.DetectReplays = false;
asbe.LocalServiceSettings.DetectReplays = false;
asbe.DefaultAlgorithmSuite = System.ServiceModel.Security.SecurityAlgorithmSuite.Basic256Rsa15;
asbe.SetKeyDerivation(false);
binding.Elements.Add(asbe);
binding.Elements.Add(new System.ServiceModel.Channels.HttpTransportBindingElement());

var endPoint = new EndpointAddress(GreetingServiceClient.EndpointAddress.Uri.ToString());
m_proxy = new GreetingServiceClient(binding, endPoint);

m_proxy.ClientCredentials.ClientCertificate.SetCertificate(System.Security.Cryptography.X509Certificates.StoreLocation.CurrentUser, System.Security.Cryptography.X509Certificates.StoreName.My, System.Security.Cryptography.X509Certificates.X509FindType.FindBySubjectName, "mobileclient");
m_proxy.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.ChainTrust;
m_proxy.ClientCredentials.ServiceCertificate.SetDefaultCertificate(System.Security.Cryptography.X509Certificates.StoreLocation.CurrentUser, System.Security.Cryptography.X509Certificates.StoreName.Root, System.Security.Cryptography.X509Certificates.X509FindType.FindBySubjectName, "mobilewcf.com");

After this, exceptions from service is not processed correctly. Is there anything I'm missing?

Thanks in advance,

Sinan
Jun 23, 2009 at 11:57 PM

Well, I found the problem. It seems SecurityVerifiedMessage class does not reflect IsFault correctly but creating a new message

from MessageBuffer handles that problem nicely. A little change i processReply method fixes the problem

(had to make reply a ref type parameter though):

        private void processReply(ref System.ServiceModel.Channels.Message reply)
        {
            System.Diagnostics.Debug.Assert((reply != null));
            // BUGFIX:
            // in mutual certificate message security mode, faults are not marked with IsFault = true
            // recreating the message sets IsFault to the correct value

            System.Diagnostics.Debug.Assert((reply != null));
            MessageBuffer buffer = reply.CreateBufferedCopy(int.MaxValue);
            reply = buffer.CreateMessage();
            if (reply.IsFault)
            {

...

Coordinator
Jul 4, 2009 at 2:55 PM

Please excuse my tardy reply here, I didn't see the digest email until now with your post, and now I see you already found it so I will fix when I return to town next week and post an update to the code! Many thanks for posting this answer!

Aug 18, 2010 at 3:19 PM

Hello!

I ran into the same problem like 'detritus' in his initial posting.
The bug fix he mentioned has no effect in my case, because the method 'processReply()' is never reached in case of an exception is thrown by the service. 
I used the sample  'MutualCertificateSecuritySoap12WSAddressing10' and changed the necessary things to make it work on my computer and on my WM6 device.
Calling the service is no problem and I receive the greeting. But if I don't pass a username, an exception is thrown on purpose by the service!
But the WM6 client only receives  the following exception:

"No signature message parts were specified for messages with the http://... fault-action"

While debugging, I set an breakpoint to the method 'processReply()' and it turns out, that this method is not reached in case of an exception thrown by the service!
In the case of an exception by the service, an exception occurs in the client in the method 'getReply()' and therefore never reaches 'processReply()':

 

     private System.ServiceModel.Channels.Message getReply(System.ServiceModel.Channels.Message msg)
        {
            if ((this.RequestChannelFactory == null))
            {
                // transport doesn't support requests
                throw new System.NotSupportedException();
            }
            System.ServiceModel.Channels.IRequestChannel requestChannel;
            System.Threading.Monitor.Enter(this.RequestChannelFactory);
            try
            {
                requestChannel = this.RequestChannelFactory.CreateChannel(this.remoteAddress);
            }
            finally
            {
                System.Threading.Monitor.Exit(this.RequestChannelFactory);
            }
            requestChannel.Open();
            try
            {
                // EXCEPTION HERE!!!
                return requestChannel.Request(msg);
            }
            finally
            {
                if ((requestChannel.State == System.ServiceModel.CommunicationState.Opened))
                {
                    requestChannel.Close();
                }
            }
        }

 

Am I missing something? I just used the code of the example without changing anything than the necessary endpoint setting etc.
The sample is wokring, but I cannot process errors thrown by the service on the WM6 device.

If i disable certificates, than I received the error thrown by the service.
What can be wrong?