消息层安全通过在通过传输通道发送消息之前对消息进行加密和签名来保证消息的机密性。在这种方式下,只有那些知道如何对消息进行解密的组织才可以读取它们。
在一些场合,消息层安全相比传输层安全来说可以提供一个更长的机密性生存周期。一个常见的例子涉及媒介。例如,当一条消息从一个客户端发送给一个服务端,如果消息实际上发送给一个媒介来执行查询或者路由功能而不是接收的终端节点,那么会发生什么?传输层安全将在消息到达媒介之前保证机密性但不会做更进一步的工作。在媒介之后,客户端失去了对机密能力的控制因为加密仅在消息到达媒介之前才使用。通过使用消息层安全,媒介可以读取消息头内容但是不能读取消息体内容。只有期望的接收方(使用此接收方的公共密钥来加密消息),才可以使用相应的私钥对消息解密并访问消息内容。在这种方式,私密性在端到端之前维护。
与传输安全类似,消息层安全也是基于X.509证书,自定义的实现也可以。服务必须安装一个证书以便于一个客户端可以给服务端发送一条加密消息来初始化通信。如果通信过程要求证书参与,那么当协商通信时这是必要的,这些证书会被保护起来。默认情况下,大多数预定义的WCF绑定,除了basicHttpBinding和netNamedPipeBinding使用消息层加密。这有助于保证默认的WCF通信是安全的。
wsHttpBinding认证模式
wsHttpBinding绑定使用消息层安全。它使用WS-Security协议并基于HTTP传输信道来在客户端和服务端之间发送加密消息。你不需要配置HTTP.SYS或者IIS来支持SSL,因为WS-Security协议在任何协议上都使能安全通信。由于这个因素,服务终结点和它的MEX邻节点可以使用同一个端口,这使得IIS寄宿变得非常简单。wsHttpBinding的一个潜在的劣势在于它为SSL使用80端口而不是443端口,这使得使用基于硬件的加速加密变得更加困难。
wsHttpBinding绑定为客户端认证支持多种方法。默认的方法是Windows认证,但是其他的选项如None, Basic和Certificate也有支持。
Windows认证模式
列表8.13显示了用来加密消息的wsHttpBinding.注意这里只有一个基地址,因为使用传输层安全所以不需要SSL信道。默认情况下,wsHttpBinding为传输安全使用Windows认证。因此,这个配置在一个局域网环境中会运行得很好因为客户端和服务端属于同一个域,但是在互联网或者跨非信任Windows机器将不会工作。
列表8.13 使用wsHttpBinding加密和Windows认证
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MyBehavior">
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="MyBehavior" name="Services.StockService">
<endpoint binding="wsHttpBinding"
contract="Services.IStockService" />
<endpoint address="mex" binding="mexHttpBinding"
contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8000/EssentialWCF" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>
无认证模式
如果你不想要任何客户端认证,为clientCredentialType属性设置None值。列表8.14显示了用来确定非客户端认证的绑定配置。
列表8.14 使用wsHttpBinding和非客户端认证的加密
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MyBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceCredentials>
<clientCertificate>
<authentication certificateValidationMode="PeerTrust" />
</clientCertificate>
<serviceCertificate findValue="WCFServerPk" x509FindType="FindBySubjectName" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="wsHttpBindingConfig">
<reliableSession enabled="true" />
<security mode="Message">
<transport>
<extendedProtectionPolicy policyEnforcement="Never" />
</transport>
<message clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
</system.serviceModel>
</configuration>
证书认证模式
使用wsHttpBinding绑定与证书认证模式可以为安全互联网应用保证一个很好的实现。配置基于证书的认证在消息层与其他认证模式类似。
列表8.15显示了使用基于证书认证的一个服务配置文件。这里有好多个需要注意的地方。首先,服务被配置为在wsHttpBinding绑定中使用clientCredentialType来请求客户端证书。其次,服务证书在<serviceCredential>节点中确定。这是必须的以便于客户端可以使用服务端的公钥来加密消息。第三,服务被配置为通过将PeerTrust设置为certificationValidationMode来绕过验证客户端证书的证书路径过程。当使用MakeCert.exe生成的证书而不是获取真正的证书或者从一个可信赖机构生成时这是必要的。
列表8.15 为客户端证书认证的服务配置
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MyBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceCredentials>
<clientCertificate>
<authentication certificateValidationMode="PeerTrust" />
</clientCertificate>
<serviceCertificate findValue="WCFServerPk" x509FindType="FindBySubjectName" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="wsHttpBindingConfig">
<reliableSession enabled="true" />
<security mode="Message">
<transport>
<extendedProtectionPolicy policyEnforcement="Never" />
</transport>
<message clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="MyBehavior" name="Services.StockService">
<endpoint binding="wsHttpBinding" bindingConfiguration="wsHttpBindingConfig"
contract="Services.IStockService" />
<endpoint address="mex" binding="mexHttpBinding" bindingConfiguration=""
contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8000/EssentialWCF" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>
为了与一个要求基于证书认证的服务通信,客户端必须为每一条消息附带证书。这可以通过代码或配置文件实现。如果通过配置文件实现,由svcutil生成的配置文件必须修改以包含证书。特别的,一个终结点行为必须被添加到确定客户端证书的位置。如果使用了非信任证书,终结点行为也必须指示应该为证书认证方法使用PeerTrust。列白哦8.16显示了一个更新了的为每条消息附加一个整数的客户端配置文件。
列表8.16 为证书认证的客户端配置
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="ClientCert">
<clientCredentials>
<clientCertificate findValue="WCFClientPK" storeLocation="LocalMachine"
x509FindType="FindBySubjectName" />
<serviceCertificate>
<authentication certificateValidationMode="PeerTrust" />
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IStockService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="true" />
<security mode="Message">
<transport clientCredentialType="Windows" proxyCredentialType="None"
realm="">
<extendedProtectionPolicy policyEnforcement="Never" />
</transport>
<message clientCredentialType="Certificate" negotiateServiceCredential="true"
algorithmSuite="Default" establishSecurityContext="true" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:8000/EssentialWCF" behaviorConfiguration="ClientCert"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IStockService"
contract="localhost.IStockService" name="WSHttpBinding_IStockService">
<identity>
<certificate encodedValue="AwAAAAEAAAAUAAAAQTFTEGK55mOxFWsNrJscyf79hBYgAAAAAQAAA
AACAAAwggH8MIIBZaADAgECAhDiu9KOnV8hmU8Z66OkxszJMA0GCSqGSIb3DQEBBAUA
MBYxFDASBgNVBAMTC1dDRlNlcnZlclBLMB4XDTEwMDIyMzA4NTAyOFoXDTM5MTIzMTI
zNTk1OVowFjEUMBIGA1UEAxMLV0NGU2VydmVyUEswgZ8wDQYJKoZIhvcNAQEBBQADgY
0AMIGJAoGBAKsGtmlX4F+SoM6xr7SWgz6BQOC0EkZWpgAV01kN7C1kY7dY1dIyI2c57
bUfxNsOiiIzLJo0ZPUj3nJ9FIudVRFFPJDuw4ZEfjDDT7OWz3xIYMfWxYFtqwpDAe9t
KgvDPbjAXXJjIXbbjYqdawKQxF0a8kIgOqbAzSGAgcbPJYSZAgMBAAGjSzBJMEcGA1U
dAQRAMD6AEFGF7EKlpfwVQOdLr8IZRMehGDAWMRQwEgYDVQQDEwtXQ0ZTZXJ2ZXJQS4
IQ4rvSjp1fIZlPGeujpMbMyTANBgkqhkiG9w0BAQQFAAOBgQA0tDO5C32L1fSBmHNF0
rHQSrVO5WNyKmLKFt42CVJSPR4J8Kq+qVIh5uIxwtVp4iVH21JLB8QM8U4UGREAM4VF
ytSfr0HylGNcMoxXNoMsZ59OKrw/Ov2N/vpI8LiClAeU7Vl6ZK0dRxGUMJN5ZVGUtEM
C+FpNCoq9+wVatMwvIw==" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
客户端证书也可以使用代码添加到服务行为中。客户端代码在本地证书存储器中寻找证书并在对服务进行调用前通过代理添加到服务行为中去。WCF将为发送到服务端的每一条消息附加证书。列表8.10显示了做这个的必要的客户端代码。
当使用svcutil来从一个不使用客户端认证的服务端生成客户端配置文件时,它在服务描述的终结点定义中插入一个<identity>元素。这个元素包含了用来生成配置文件的服务的签名。在运行时,签名用来检查运行服务的身份。如果客户端尝试与一个不同签名的服务通信,将会抛出一个错误:
“’目标终结点’http://localhost:8000/EssentialWCF’ 期待的身份是‘身份(http://schemas.xmlsoap.org/ws/2005/05/identity/right/possesspropery: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/thumbprint )‘”