[WCF]Secure a dynamically added message header via behavior extension(part 2)


[WCF]Secure a dynamically added message header via behavior extension(part 2)


 


This article is the continue of my previous one demonstrating how to secure a custom message header added dynamically in WCF message. In the previous part 1, I use a custom ContractBehavior to inject the security protectionRequirement that is necessary for securing our dynamically added message header.


 


In this one, I will provide the example that use a custom endpointBehavior to inject the protection requirement for securing dynamic message header. Things different from part1 include:


 


ü  Use endpointBehavior instead of contractBehavior to customize runtime


ü  Use an untyped messageheader instead of a user defined typed header


ü  Use code to initialize the WCF runtime instead of configuration file(both client and service)


ü  Use channelFactory(instead of auto-generated service proxy) to consume service


 


Here are complete code of the three projects(the same as part1):


 


 


l  Shared Library


This class library(referenced by both service and client app) include a service contract and a custom endpointbehavior.


 






namespace SharedLib


{


    [ServiceContract]


    public interface ITestService


    {


        [OperationContract]


        string GetData();


    }


/// <summary>


    /// my custom endpointBehavior class


    /// </summary>


    public class SecureHeaderEndpointBehavior : IEndpointBehavior


    {


        public string HeaderName { get; set; }


        public string HeaderNamespace { get; set; }


        public ProtectionLevel HeaderProtecionLevel { get; set; }


 


        public SecureHeaderEndpointBehavior() { }


 


        public SecureHeaderEndpointBehavior(string headerName, string headerNamespace, ProtectionLevel pLevel)


        { HeaderName = headerName; HeaderNamespace = headerNamespace; HeaderProtecionLevel = pLevel; }


 


        #region IEndpointBehavior Members


 


        public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)


        {


            ChannelProtectionRequirements requirements = bindingParameters.Find<ChannelProtectionRequirements>();


 


//identifier for my custom message header


            XmlQualifiedName qnHeader = new XmlQualifiedName(HeaderName, HeaderNamespace); 


 


            //set protectionLevel


            if(HeaderProtecionLevel != ProtectionLevel.None)


            {


                requirements.IncomingSignatureParts.ChannelParts.HeaderTypes.Add(qnHeader);


 


                if (HeaderProtecionLevel == ProtectionLevel.EncryptAndSign)


                    requirements.IncomingEncryptionParts.ChannelParts.HeaderTypes.Add(qnHeader);


            }


        }


 


        public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)


        {}


        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)


        {}


        public void Validate(ServiceEndpoint endpoint)


        {}


        #endregion


    }


}


 


 


l  WCF Service


The service provides a simple implementation of the shared service contract:


 






public class SimpleTestService :ITestService


    {


        #region ITestService Members


        public string GetData()


        {


            string data = “”;


 


            OperationContext oc = OperationContext.Current;


            int i = oc.IncomingMessageHeaders.FindHeader(“MyDynamicHeader”, “urn:test”);


            XmlDictionaryReader reader = oc.IncomingMessageHeaders.GetReaderAtHeader(i);


 


            data = “data from MyDynamicHeader: “ + reader.ReadString();


 


            return data;


        }


 


        #endregion


    }


 


And here is the hosting code:


 






static void RunService()


        {


            string baseUrl = “http://localhost:11111/TestService”;


 


            using (ServiceHost host = new ServiceHost(typeof(SimpleTestService), new Uri(baseUrl)))


            {


                //configure service


                ServiceMetadataBehavior smb = new ServiceMetadataBehavior(){ HttpGetEnabled = true};


                host.Description.Behaviors.Add(smb);


 


                WSHttpBinding binding = new WSHttpBinding( SecurityMode.Message);


                binding.Security.Message.ClientCredentialType = MessageCredentialType.Windows;


 


                ServiceEndpoint sep = host.AddServiceEndpoint(typeof(ITestService), binding, “”);


 


                host.Open();


 


 


                Console.WriteLine(“service has started………..”);


                Console.ReadLine();


 


            }


        }


 


l  WCF Client


The client application will use channelFactory to consume WCF service. It first inject our custom endpointBehavior and insert an untyped messageHeader before calling method.


 






static void CallService()


        {


            //init client service proxy


            string epAddress = “http://localhost:11111/TestService”;


 


            WSHttpBinding binding = new WSHttpBinding(SecurityMode.Message);


            binding.Security.Message.ClientCredentialType = MessageCredentialType.Windows;


 


            ChannelFactory<ITestService> factory = new ChannelFactory<ITestService>(


                binding,


                epAddress);


 


            //here we inject our endpoint behavior into the client side proxy


            factory.Endpoint.Behaviors.Add(


                new SecureHeaderEndpointBehavior(


                    “MyDynamicHeader”,


                    “urn:test”,


                    ProtectionLevel.Sign


                    ));


      


            ITestService client = factory.CreateChannel();


 


 


            using (OperationContextScope scope = new OperationContextScope((IContextChannel)client))


            {


                OperationContext.Current.OutgoingMessageHeaders.Add(


                    MessageHeader.CreateHeader(


                        “MyDynamicHeader”,


                        “urn:test”,


                        “Some test data…”


                        ));


 


                string data = client.GetData();


 


                Console.WriteLine(data);


 


            }


        }