In the last post, I discussed about the problem not being able to configure ProtectionLevel for different endpoint declaratively through configuration. However, it might be a very good requirement for a WCF service and I will show you how. In my today’s post I will go through such a case where I would preferably like to configure ProtectionLevel at runtime through configuration rather imperative way as it is not available in standard Wcf application for bindings like wsHttpBinding and basicHttpBinding & so on.
Why configure ProtectionLevel dynamically thru configuration?
Basically, ProtectionLevel enforce security requirement of request and response messages in the channel and all the stakeholder of the message must conform to the requirement; anything otherwise results in exception. Last post, I showed you, how to configure ProtectionLevel in different level of WCF messaging stack and saw that it can only be set it programmatically in the contract of the service which has impact on all the preconfigured bindings.
Now, let’s say, I want to have 2 different endpoint to use different ProtectionLevel.
In addition, I also want to make it configurable so that I can change the security behavior of the endpoint conveniently after it has been deployed.
Why would I want that? Good question. Consider following case when I have only one endpoint and I am using message level security with wsHttpBinding (or ws2007httpbinding preferable one for internet based Wcf Service). For internet users consuming this service, I am using ProtectionLevel.EncryptAndSign because of security requirement of our application.
However, in case of our intranet, I don’t want to take the overhead of
ProtectionLevel.EncryptAndSign rather would like to use ProtectionLevel.Sign to make the service a bit more responsive and efficient by getting rid of the overhead of encryption of request and response. And most importantly, I don’t need ProtectionLevel.EncryptAndSign for the messages in users in Intranet as per security requirement. Out of the box, there is no features available that will enable to use different ProtectionLevel in these 2 cases. Duh! Obviously there is one option to host to service twice by compiling the code in 2 different ProtectionLevel. At 1 to 10 scale, how would you rate this solution ? J Ok , then let’s move on…

So, what we need is – make the 2 different endpoints to work with different ProtectionLevel and using custom endpoint behavior , it can be easily done. In that case, internet users will use the wsHttpBinding with default ProtectionLevel and the intranet user will use less secure

ProtectionLevel.Sign. In the next section, I will explain step by step how to do it.
How to configure ProtectionLevel dynamically thru configuration?
I hope, by this time it is clear , why we need it to configure it dynamically. If you have question, please let me know.
-
First create a Custom EndpointBehavior by implementing IEndpointBehavior as below –
public class MessageSecurityBehavior:IEndpointBehavior
{
public ProtectionLevel ProtectionLevel { get; set; }#region IEndpointBehavior Members
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
//Do nothing
}public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
//Do nothing
}public void Validate(ServiceEndpoint endpoint)
{
//Do nothing
}public void AddBindingParameters(ServiceEndpoint serviceEndpoint, BindingParameterCollection parameters)
{
//Setting the ProtectionLevel at the Service Contract
serviceEndpoint.Contract.ProtectionLevel = ProtectionLevel;parameters.Remove
(); ChannelProtectionRequirements requirements = new ChannelProtectionRequirements();
parameters.Add(requirements);MessagePartSpecification unprotectedBody = new MessagePartSpecification();
MessagePartSpecification protectedBody = new MessagePartSpecification(true);switch (ProtectionLevel)
{
case ProtectionLevel.None:
requirements.OutgoingSignatureParts.AddParts(unprotectedBody, “*”);
requirements.IncomingSignatureParts.AddParts(unprotectedBody, “*”);
requirements.OutgoingEncryptionParts.AddParts(unprotectedBody, “*”);
requirements.IncomingEncryptionParts.AddParts(unprotectedBody, “*”);
break;
case ProtectionLevel.Sign:
requirements.OutgoingSignatureParts.AddParts(protectedBody, “*”);
requirements.IncomingSignatureParts.AddParts(protectedBody, “*”);requirements.OutgoingEncryptionParts.AddParts(unprotectedBody, “*”);
requirements.IncomingEncryptionParts.AddParts(unprotectedBody, “*”);
break;
case ProtectionLevel.EncryptAndSign:
requirements.OutgoingSignatureParts.AddParts(protectedBody, “*”);
requirements.IncomingSignatureParts.AddParts(protectedBody, “*”);
requirements.OutgoingEncryptionParts.AddParts(protectedBody, “*”);
requirements.IncomingEncryptionParts.AddParts(protectedBody, “*”);
break;
}
}#endregion
}Listing 1
-
Create a BehaviorElement by extending BehaviorExtensionElement to make the behavior configurable through config file.
///
/// Represents Element to set the ProtectionLevel to different Endpoint using configuration
///
public class MessageSecurityBehaviorElement : BehaviorExtensionElement
{
private const string PROTECTION_LEVEL_ELEMENT_NAME = “messageProtection”;
public override Type BehaviorType
{
get
{
return typeof(MessageSecurityBehavior);
}
}protected override object CreateBehavior()
{
return new MessageSecurityBehavior { ProtectionLevel = this.ProtectionLevel };
}[ConfigurationProperty(PROTECTION_LEVEL_ELEMENT_NAME)]
public ProtectionLevel ProtectionLevel
{get
{
return (ProtectionLevel)base[PROTECTION_LEVEL_ELEMENT_NAME];
}set
{
base[PROTECTION_LEVEL_ELEMENT_NAME] = value;
}}
private ConfigurationPropertyCollection properties = null;
protected override ConfigurationPropertyCollection Properties
{
get
{
if (this.properties == null)
{ConfigurationPropertyCollection propertys = new ConfigurationPropertyCollection();
propertys.Add(new ConfigurationProperty(PROTECTION_LEVEL_ELEMENT_NAME, typeof(ProtectionLevel), null, ConfigurationPropertyOptions.IsRequired));
properties = propertys;
}
return properties;
}}
}
Listing 2
-
So, seems like coding part is done. Let’s start configuring it. To do so, first thing that needs to be done is to add a behaviorExtensions inside system.serviceModel > behaviorExtensions specifying the newly created custom Endpoint behavior :
<system.serviceModel> <extensions> <behaviorExtensions> <add name="messageProtection" type="TestService. MessageSecurityBehaviorElement, TestService"/> </behaviorExtensions> </extensions> <!--Rest of the configuration--> </system.serviceModel>Listing 3
-
Then, create a endpoint behavior like below :
<system.serviceModel> <behaviors> <endpointBehaviors> <behavior name="noneProtectionLevelForEndPoint"> <messageProtection protectionLevel="None"/> </behavior> </endpointBehaviors> </behaviors> <!--Rest of the configuration--> </system.serviceModel>Listing 4
-
Now, if I need to use this kind of ProtectionLevel that I configured at step 4 in any endpoint , I just need to add it as behaviorConfiguration. and I am done.
<system.serviceModel> <!-- Rest of the configuration--> <endpoint address="wsHttp" binding="wsHttpBinding" name="wsBinding.ModeMessage.CredentialNone.BindingName" contract="TestService.ITestService" behaviorConfiguration="noneProtectionLevelForEndPoint"> <identity> <dns value="localhost"/> </identity> </endpoint> </system.serviceModel>Listing 5
A bit details about the MessageSecurityBehavior:
So, now since I just showed you how to configure ProtectionLevel at runtime , let’s go for bit more explanation of MessageSecurityBehavior ; only if you are interested. By changing the ChannelProtectionRequirement of the Endpoint, the new custom behavior impacts the request and response in the channel. Moreover , the contract also binds to the configured ProtectionLevel.
Then 2 different MessagePartSpecification was created where first 1st one is a empty MessagePartSpecification and 2nd one refers to the MessagePartSpecification which contains body.

Then, depending of different value ProtectionLevel, the MessagePartSpecification are set to the ChannelProtectionRequirements. 
For example, in case ProtectionLevel.Sign , in OutgoingSignatureParts and IncomingSignatureParts of ChannelProtectionRequirements, MessagePartSpecification that included Body is being added to be signed from client to the server and again back to client from server. However, in this case , encryption is not needed , so in OutgoingEncryptionParts and IncomingEncryptionParts, empty MessagePartSpecification is added and that results in unencrypted messages.
Conclusion
It’s not difficult to update the ProtectionLevel
at runtime, but bear in mind that client and server always have to conform to the ProtectionLevel
requirement, which means, updating the ProtectionLevel
requirement at runtime might results in updating the clients configuration/code. It’s a great tool, use it with care J. Any pops up, just give me a buzz.
Additional Links
- WCF Security: WCF Performance & ProtectionLevel – Part 1 : http://adilakhter.wordpress.com/2009/08/06/wcf-security-wcf-performance-protectionlevel-part-1/
- Custom WCF Behaviors through App.Config :http://winterdom.com/2006/10/customwcfbehaviorsthroughappconfig
- Configuring ProtectionLevel : http://blogs.msdn.com/drnick/archive/2008/03/10/configuring-protection-level.aspx
- Fundamentals of WCF Security : http://www.code-magazine.com/article.aspx?quickid=0611051





