08/10/2007
How to set ProtectionLevel from config
Protectionlevel enum defines the level of security in terms of encryption & signature. When programing at the Service Model layer, ProtectionLevel can be set at various levels:
- ServiceContract level: all messages/faults of the service contract will have same protection level
- OperationContract level: An operation can override the protection level specified at the contract level. So messages/faults of an operation can have different protection requirements.
- MessageContract level: Individual messages/faults can also specify their own protection level, overriding the one specified at higher level.
[ServiceContract(Namespace = "http://www.zamd.net/Calcualtor", ProtectionLevel = ProtectionLevel.Sign)]
public interface ICalculator
{
[OperationContract]
int Add(int a, int b);
}
all good so far, however there are couple of limitation with the current implementation.
- ProtectionLevel can only be set in the code. Its quite common requirement that operations/IT want to control the protection level as part of the deployment. Hard coding protection level in code as an attribute is quite in-efficient in that case. As you have to recompile to change the protection level.
- Another more subtle requirement I have seen is to have the ability to have different Protectionlevel for different endpoints sharing the same contract. e.g an intranet endpoint and an internet endpoint sharing the same contract. You might want to only sign the messages for intranet communication while both singing and encrypting for internet communication. Currently because Protectionlevel is specified as part of the contract, which gets shared by multiple endpoints. There is no built-in way to achieve this other than creating multiple contracts.
If your only requirement is to be able to set protection level from config file, then a simple option could be to access the description object model and update the contract protection level there before opening the service host or proxy object.
ServiceHost svc = new ServiceHost(typeof(CalculatorService));
svc.Description.Endpoints[0].Contract.ProtectionLevel = GetProtectionLevelFromConfig();
If however, you want to set different protection level for different endpoints, then you have to do some more work.
Specifically you have to create an endpoint behavior, from which you access ChannelProtectionRequirements parameter to specify endpoint specific protection requirements for that endpoint. Here is step by step procedure to do this:
1. Create an endpoint behavior and add following code to AddBindingParameters method.
public class ProtectionLevelBehavior : IEndpointBehavior
{
ProtectionLevel level;
internal ProtectionLevelBehavior(ProtectionLevel level)
{
this.level = level;
}
#region IEndpointBehavior Members
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
var proReq =
bindingParameters.Remove<ChannelProtectionRequirements>();
proReq = new ChannelProtectionRequirements();
MessagePartSpecification unProtectedSpec = new MessagePartSpecification();
MessagePartSpecification protectedSpec = new MessagePartSpecification(true);
//I'm setting same protection level for all the actions.
// You could specify different protection level per action, if required.
// Also note, I haven't implemented any support for custom SOAP headers.
// However that can easily be added using the same mechansim.
switch (level)
{
case ProtectionLevel.None:
proReq.OutgoingSignatureParts.AddParts(unProtectedSpec, "*");
proReq.IncomingSignatureParts.AddParts(unProtectedSpec, "*");
proReq.OutgoingEncryptionParts.AddParts(unProtectedSpec, "*");
proReq.IncomingEncryptionParts.AddParts(unProtectedSpec, "*");
break;
case ProtectionLevel.Sign:
proReq.OutgoingSignatureParts.AddParts(protectedSpec, "*");
proReq.IncomingSignatureParts.AddParts(protectedSpec, "*");
proReq.OutgoingEncryptionParts.AddParts(unProtectedSpec, "*");
proReq.IncomingEncryptionParts.AddParts(unProtectedSpec, "*");
break;
case ProtectionLevel.EncryptAndSign:
proReq.OutgoingSignatureParts.AddParts(protectedSpec, "*");
proReq.IncomingSignatureParts.AddParts(protectedSpec, "*");
proReq.OutgoingEncryptionParts.AddParts(protectedSpec, "*");
proReq.IncomingEncryptionParts.AddParts(protectedSpec, "*");
break;
}
// Add our protection requirement for this endpoint into the binding params.
bindingParameters.Add(proReq);
}
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
}
2. Now create a behavior extension so that this behavior can be enabled/disabled using configuration file. The behavior extension will read the configured protectionLevel from config file and will create an instance of above endpoint behavior to plug it into behavior collections.
//behavior configuration extension element
public class ProtectionLevelBehaviorElement : BehaviorExtensionElement
{
public ProtectionLevelBehaviorElement()
{
}
public override Type BehaviorType
{
get { return typeof(ProtectionLevelBehavior); }
}
protected override object CreateBehavior()
{
return new ProtectionLevelBehavior(this.Level);
}
[ConfigurationProperty("level")]
public ProtectionLevel Level
{
get
{
return (ProtectionLevel)base["level"];
}
set
{
base["level"] = value;
}
}
ConfigurationPropertyCollection properties = null;
protected override ConfigurationPropertyCollection Properties
{
get
{
if (this.properties == null)
{
ConfigurationPropertyCollection propertys = new ConfigurationPropertyCollection();
propertys.Add(new ConfigurationProperty("level", typeof(ProtectionLevel), null, ConfigurationPropertyOptions.IsRequired));
this.properties = propertys;
}
return this.properties;
}
}
}
3. And this is how you will specify the protection level from config file using the above behavior extension.
<behaviors>
<endpointBehaviors>
<behavior name="endpointSecurityConfig">
<protectionLevel level="Sign"/> <!-- None, Sign, EncryptAndSign-->
</behavior>
</endpointBehaviors>
</behaviors>
Complete Source
enjoy :)