[WCF安全系列]消息的保护等级[下篇]

在《上篇》中,我们着重讨论了消息的保护等级如果在契约中定义,定义在不同契约(服务契约、错误契约和消息契约)中的消息保护等级具有怎样的层级关系,以及在默认情况下各种绑定采用怎样的保护等级。在下篇中,我们进一步来探讨消息保护等级和绑定的关系。

一、契约的保护等级为绑定进行消息保护设置了“最低标准”
二、显式地将保护等级设置成ProtectionLevel.None与没有设置保护等级有区别吗?
三、消息的保护等级与WS-Addressing

一、契约的保护等级为绑定进行消息保护设置了“最低标准”

定义在契约上消息保护级别实际上为WCF实施消息保护设置了一个“最低标准”。由于整个消息保护机制,不论是签名还是加密,都是在信道层实现的。而信道层最终是通过绑定来实现的,绑定的属性决定了信道层处理消息的能力。而绑定安全方面的属性自然就决定了最终的信道层是否有能力对消息实施签名和加密。一方面,以契约形式定义的消息保护级别帮助信道层决定应该对传入的消息采取那个级别的保护机制;另一方面,如果绑定所能提供的消息保护能力不能达到这个最低标准,就会抛出异常。

举个例子,如果我们通过如下的代码将服务契约ICalculator的Add操作的保护级别设置成EncryptAndSign。

   1: [ServiceContract(Namespace = "http://www.artech.com/")]
   2: public interface ICalculator
   3: {
   4:     [OperationContract(ProtectionLevel = ProtectionLevel.EncryptAndSign)]
   5:     double Add(double x, double y);
   6: }  

但是我们确将终结点使用到的WS2007HttpBinding的安全模式设置成None。那么在对服务进行寄宿的时候,就会跑出如下图所示的InvalidOperationException异常,提示“必须保护请求消息”。

   1: <system.serviceModel>
   2:   <bindings>
   3:     <ws2007HttpBinding>
   4:       <binding name="bindingWithNoneSecurityMode">
   5:         <security mode="None"/>
   6:       </binding>
   7:     </ws2007HttpBinding>
   8:   </bindings>
   9:   <services>
  10:     <service name="Artech.WcfServices.Services.CalculatorService" >
  11:       <endpoint address="http://127.0.0.1/calculatorservice" binding="ws2007HttpBinding" bindingConfiguration="bindingWithNoneSecurityMode" 
  12: contract="Artech.WcfServices.Contracts.ICalculator"/>
  13:     </service>
  14:   </services>
  15: </system.serviceModel>

二、显式地将保护等级设置成ProtectionLevel.None与没有设置保护等级有区别吗?

在这里有一个很多人会忽视的要点。表示消息保护级别的ProtectionLevel类型是一个枚举,所以它肯定有一个默认值。这个默认值就是None,也就是说当你没有显式地指定契约具有采用那么保护级别的时候,默认值就是None。但是这种情况和你显式保护级别设置为None的效果是完全不一致的。因为前者真正采用的保护级别(当绑定安全被开启)实际上是EncryptAndSign,后者才是None。那么WCF如何来区分这两种情况呢?

如果你足够细心,你应该会发现:在上面介绍的定义消息保护级别的特性中,除了具有一个可读可写的ProtectionLevel属性之外,还具有一个只读的HasProtectionLevel属性,该属性表示你是否对消息保护级别进行了“显式”的设置。我们可以通过一个简单的实验来演示HasProtectionLevel的作用。

下面我定义了两个服务契约IServiceContract1和IServiceContract2,其实前者没有对ProtectionLevel进行相应的设置,后者被显式地设置为None。

   1: [ServiceContract]
   2: public interface IServiceContract1
   3: {
   4:     [OperationContract]
   5:     void DoSomething();
   6: }
   7: [ServiceContract(ProtectionLevel = ProtectionLevel.None)]
   8: public interface IServiceContract2
   9: {
  10:     [OperationContract]
  11:     void DoSomething();
  12: }

然后我编写了如下的代码,基于上面两个接口类型生成相应的ContractDescription对象,然后将它们的ProtectionLevel和HasProtectionLevel属性输出来。从最终的输出结果我们可以很清楚地看到:两种情况下下ProtectionLevel属性值都是None,但是只有当你显式地设置了ProtectionLevel的情况下,HasProtectionLevel属性才会返回True。WCF就是根据ContractDescription的这两个属性决定最终采用怎样的消息保护级别的。

   1: ContractDescription contract1 = ContractDescription.GetContract(typeof(IServiceContract1));
   2: ContractDescription contract2 = ContractDescription.GetContract(typeof(IServiceContract2));
   3:  
   4: Console.WriteLine("{0,-10}{1,-20}{2,-20}", "Contract","ProtectionLevel", "HasProtectionLevel");
   5:  
   6: Console.WriteLine("{0,-10}{1,-20}{2,-20}", "contract1", contract1.ProtectionLevel, contract1.HasProtectionLevel);
   7: Console.WriteLine("{0,-10}{1,-20}{2,-20}", "contract2", contract2.ProtectionLevel, contract2.HasProtectionLevel);

输出结果:

   1: Contract  ProtectionLevel     HasProtectionLevel
   2: contract1 None                False
   3: contract2 None                True

三、消息的保护等级与WS-Addressing

关于消息保护级别与绑定的关系,还有一点需要着重强调。虽然我们可以对于同一个服务契约下操作设置不同的保护级别,但是在WSDL中需要基于WS-Addressing中的寻址(Addressing)机制来识别基于操作的保护级别。在使用的绑定不支持WS-Addressing的情况下(比如BasicHttpBinding),它会选择所有操作中等级最高的那个作为所有操作的保护级别。比如说对于如下定义的服务契约ICalculator,在使用BasicHttpBinding的情况下,两个操作采用的保护级别都是EncryptAndSign。

   1: [ServiceContract]
   2: public interface ICalculator
   3: {
   4:     [OperationContract(ProtectionLevel = ProtectionLevel.Sign)]
   5:     double Add(double x, double y);
   6:     [OperationContract(ProtectionLevel = ProtectionLevel.EncryptAndSign)]
   7:     double Substract(double x, double y);
   8: }

这实际上会为你的应用带来一个很隐晦的问题,为了将这个问题阐述得更加清楚,我通过一个例子来说明。还是应用我们的计算服务的例子,下面是我们再熟悉不过的服务契约的定义,Add操作的保护级别被设置成Sign。

   1: [ServiceContract(Namespace = "http://www.artech.com/")]
   2: public interface ICalculator
   3: {
   4:     [OperationContract(ProtectionLevel = ProtectionLevel.Sign)]
   5:     double Add(double x, double y);
   6: }

但是这个服务契约并被客户端共享,而客户端服务契约中定义了一个额外的操作Substract,该操作的保护级别并未作显式设置。

   1: [ServiceContract(Namespace = "http://www.artech.com/")]
   2: public interface ICalculator
   3: {
   4:     [OperationContract(ProtectionLevel = ProtectionLevel.Sign)]
   5: double Add(double x, double y);
   6:     [OperationContract]
   7:     double Substract(double x, double y);
   8: }

现在选择BasicHttpBinding作为终结点的绑定,并将安全模式甚至成Message。当你客户端调用Add操作的时候。会抛出如下图所示的MessageSecurityException异常,提示“主签名必须加密”。但是当你将客户端Substract删除或者将Substract操作的消息保护级别也设置成Sign是,这个异常将不会出现。

出现这样的异常的原因在于:对于不支持WS-Addressing的BasicHttpBinding来说,会选择所有操作中等级最高的那个最为所有操作的保护级别。对于客户端来说,由于Substract没有对保护级别进行显式设置,默认采用最高等级的EncryptAndSign。但是服务端的等级确是Sign。

在这种情况下,请求消息会同时被加密和签名。请求消息被服务端接受之后,虽然它对应的等级是Sign,但是依然能够处理该请求。这就是所谓的“消息保护级别的最低标准”原则,定义在契约中的保护级别只是确立了一个消息保护的“底线”。你不能低于这个最低标准,但是可以高于它。但是服务执行正常的运算后,只会按照定义在本地契约中设置的保护级别对回复消息进行签名。客户端接受到这个仅仅被签名的回复消息,会发现等级不够,所以才会提示你“主签名必须加密”。

消息的保护等级[上篇]

消息的保护等级[下篇]

作者:蒋金楠 

微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

原文链接

时间: 2024-11-09 21:58:31

[WCF安全系列]消息的保护等级[下篇]的相关文章

[WCF安全系列]消息的保护等级[上篇]

到目前为止,对于WCF安全传输的三个方面,我们已经对认证进行了详细的介绍,现在我们来关注另外两个话题:消息的一致性和机密性,两者又统称为消息保护(Message Protection).消息的安全等级指的是对整个消息或者消息的某个部分事实安全保护采用的等级.按照级别的由低到高,WCF支持如下三种不同的安全等级.在WCF的应用编程接口中,消息保护级别通过如下定义的ProtectionLevel枚举表示. None:不采用任何措施来保护消息的一致性和机密性: Sign:通过对整个消息或者消息的某个部

WCF专题系列(7):消息如何传递之绑定Part 2

概述 每个服务终结点都包含一个地址Address.一个绑定Binding 和一个 契约Contract.契约指定可用的操作,绑定指定如何与服务进行通信,而地址指 定查找服务的位置,在WCF专题系列前5篇中,深入了解了WCF中寻址的细节.本 文为消息如何传递之绑定第二部分,将详细介绍WCF内置的各种绑定元素,绑定 元素之间的顺序以及如何创建一个自定义的绑定元素. 在WCF专题系列( 6):消息如何传递之绑定Part 1一文中,我提到绑定由绑定元素组成,每个绑 定元素用来描述终结点与客户端通信方式中

WCF专题系列(4):深入WCF寻址Part 4—自定义消息筛选器

概述 在WCF专题系列(3):深入WCF寻址Part 3-消息过滤引擎一文 中,详细介绍了WCF中的消息筛选引擎,包括消息筛选器和筛选器表,每个 EndpointDispatcher都包含了两个消息筛选器,默认的地址过滤器是 EndpointAddressMessageFilter,默认的契约过滤器是ActionMessageFilter, 这些是可以通过Behavior来改变的.本文我们将学习如何创建一个自定义的消息 过滤器,并通过自定义Behavior来改变EndpointDispatche

WCF专题系列(2):深入WCF寻址Part 2—自定义寻址报头

在WCF专题系列(1):深入WCF寻址Part1一文中,我们对Web服务寻址规范做 了一些认识,了解了终结点引用和消息信息报头两种结构,该规范在Web服务中 的地位举足轻重,后续我们会经常提到该规范.在本文中,我们将继续深入WCF 寻址的内容,包括元数据中的终结点地址,自定义寻址标头等相关信息. 终结点地址定义 了解了Web服务寻址规范,再回到WCF,在WCF中,终 结点地址是由EndpointAddress类来表示的,它其中很重要的几个部分是:一个 表示服务地址的统一资源定位符 (URI),一

WCF专题系列(1):深入WCF寻址Part 1

概述 众所周知,WCF服务的所有通信都是通过服务的终结点发生的,每个 服务终结点都包含一个地址Address.一个绑定Binding 和一个契约Contract. 契约指定可用的操作,绑定指定如何与服务进行通信,而地址指定查找服务的位 置,即非常经典的"ABC".WCF用多种不同的通信协议为公开服务终 结点和与其通信提供了灵活的模式,在WCF专题系列的第一部分,我将围绕终结 点的寻址细节展开讨论,再此之前,我们先看一下WCF的编程模型,如图1所示: 图1 Web服务寻址规范 在 WCF

[WCF权限控制]ASP.NET Roles授权[下篇]

为了让读者对基于ASP.ENT Roles授权方式有一个全面的认识,我们现在来做一个实例演示.在这个实例中,我们将采用不同的认证方式,包括Windows认证和证书认证(ASP.NET Membership + Roles为常见的组合方式,在这里就不多作演示).简单起见,我们依然沿用一贯的基于如下图所示的解决方案结构,并且依然采用声明式的授权.所以在服务操作方法Add上通过应用PrincipalPermissionAttribute特性指定其被授权的角色Administrators. 1: pub

[老老实实学WCF] 第九篇 消息通信模式(上) 请求应答与单向

原文:[老老实实学WCF] 第九篇 消息通信模式(上) 请求应答与单向 老老实实学WCF 第九篇 消息通信模式(上) 请求应答与单向   通过前两篇的学习,我们了解了服务模型的一些特性如会话和实例化,今天我们来进一步学习服务模型的另一个重要特性:消息通信模式.   WCF的服务端与客户端在通信时有三种模式:单向模式.请求/应答模式和双工模式.   如果选用了单向模式,调用方在向被调用方进行了调用后不期待任何回应,被调用方在执行完调用后不给调用方任何反馈.如客户端通过单向模式调用了一个服务端的操作

[WCF安全系列]通过绑定元素看各种绑定对消息保护的实现

目录 一.BasicHttpBinding 二.WSHttpBinding.WS2007HttpBinding和WSDualHttpBinding 三.NetTcpBinding和NetNamedPipeBinding 四.NetMsmqBinding 五.总结 在本系列中我不断在强调这么一个要点:整个安全传输的实施最终是在信道层实现的.而信道层是根绝终结点绑定创建的,而绑定从结构上是一系列绑定元素的有序集合.当绑定的安全开启的时候,决定最终安全传输实现方式的必然是某一个或者多个绑定元素.了解相

WCF专题系列(6):消息如何传递之绑定Part 1

什么是绑定 从本质上说,WCF是一个通信服务框架,它允许我们使用不同 的传输协议(如Http.Https.TCP.P2P.IPC和MSMQ等),使用不同的消息编码 形式(文本.二进制和MTOM),跟不同的WS-*系列规范(如WS-Security.WS- Atomic Transaction等)交互.所有这些细节都是由通道堆栈来处理的,看一下 Aaron Skonnard给出的这幅图: 图1 最底层传输组件读入消息,消息编码器将传入字节读取为 逻辑消息对象,之后消息通过通道堆栈中的各个消息,它们