前几天成功建立起了一套基于证书的安全的WCF服务,很兴奋,现在做一下总结,希望大侠们指点,也希望能帮助到一些菜鸟,哈哈。


一、安全通信的基本过程


这里只做定性的理论上的分析,因为再详细一点的我也不懂。一个通信分为客户端A和服务端B,A发送请求,B响应请求。安全通信必须满足3个方面的要
求,1是保密性,即数据要经过加密,防止第三者偷窥到。2是防篡改(不知道用完整性来表示合适不),即保证B收到的数据与A发出的数据是一样的,3是抗否
认,即如果A向B发送了数据,要从技术上保证A没办法抵赖说A没发,或者说A发的与B收到的不一样。2和3从某种意义上是同义词,因为既然没人能冒充A发
数据,那么A就不能抵赖。


二、PKI系统


PKI系统由信息发送方,接收方,和CA(证书认证中心)3部分组成。PKI系统可以满足以上安全通信的所有要求。


发送方A和接收方B都有一对密钥,A有公钥A和私钥A,B有公钥B和私钥B,这些密码是由CA签发的,A和B都信任CA,信任的关系是建立在证书链
的基础上的,CA有自己的根证书Root(包含公钥Root和私钥Root),A和B的密钥实际上是由CA根据自己的密钥所签发的子密钥。PKI系统实现
了这样一个逻辑,就是如果一个PC信任一个Root证书,那么它也信任由Root签发的子证书。这一逻辑由系统软件(比如浏览器、WCF框架)所实现,我
们不必实现和管理此逻辑。所以,A和B机里的受信任的根证书区里都要包含Root的公钥,此外A和B都有自己的公钥和私钥,因为A和B都信任Root,所
以这样配置下来,A就会信任B,B也会信任A。


(1)发送信息I之前,A首先生成某种对称加密算法(比如DES,AES)的密钥Q,Q是用来加密信息I的,因为对称加密比非对称加密快很多,所以虽然我们可以用私钥A来对I加密,但却不这么做。


(2)A用公钥B对Q加密,生成Q’。


(3)A用Q对I加密,生成I’。


(4)A用私钥A做签名。具体过程是:先对(Q’+I’)做哈希(MD5、SHA1等)得S,再用私钥A对S做加密,得S’。


(5)A把(Q’+I’+S’)发给B。


(6)B用公钥A对签名做验证。具体过程是:先对((Q’+I’)做哈希(MD5、SHA1等)得S,再用公钥A对S’做解密得S’’,看S是否等于S’’,相等则有效,不等则验证失败。


(7)B用私钥B对Q’做解密得Q。


(8)用Q对I’做解密,得I。至此,A完成了向B发送I,B完成了接收I。


在第5步,信息流传于公网上,所以(Q’+I’+S’)可能被黑客X所截获,但我们可以证明,X不可能获得有用的信息(即保密性),X也不能篡改I并发给B(防篡改,检测篡改),A也不能抵赖(抗否认):


1.
X要想知道I,则必须先知道Q,但它只知道Q’,所以它还要知道私钥B才能得到Q,但这是不可能的,因为只有B和CA才知道私钥B,而CA是绝对公正和安全的,B又不会把私钥B告诉别人。


2.
如果第(6)步验证成功,我们可以断定I没有被篡改过。因为假如I被篡改过,则签名S’经过公钥A解密后得的S’’,一定不会等于(Q’+I’)的哈希值S,而这与已知的条件不符,所以是不可能的。


3.
假如A要想否认自己发过I,则说明A可能把私钥A泄露给了别人Y,Y就是坏蛋。如果A说没有告诉别人,则A就在说谎,因为签名S’只有A才能生成。所以A不能抵赖。


三、一个Case


有3台PC(W,Y,P),W为客户端,W请求Y,Y又请求P,那么Y即是服务端又是客户端,P是服务端,Y和P上布署的有WCF。


要保证这三台PC间的安全通信还必须有个CA,我们可以自己架设,也可以买第三方的服务。在开发阶段,我们可以用makecert来模拟CA签发证书的过程。


具体过程如下:


(1)生成根证书


makecert -n "CN=TempCA" -r -sv TempCA.pvk TempCA.cer


TempCA就是模拟的Root证书


(2)生成W,Y,P子证书


makecert -n "CN=W" -iv TempCA.pvk -ic TempCA.cer -sv W.pvk W.cer -pe -sky
exchange
pvk2pfx.exe -pvk W.pvk -spc W.cer -pfx W.pfx


==========================================================
makecert -n
"CN=Y" -iv TempCA.pvk -ic TempCA.cer -sv Y.pvk Y.cer -pe -sky
exchange
pvk2pfx.exe -pvk Y.pvk -spc Y.cer -pfx Y.pfx


==========================================================
makecert -n
"CN=P" -iv TempCA.pvk -ic TempCA.cer -sv P.pvk P.cer -pe -sky
exchange
pvk2pfx.exe -pvk P.pvk -spc P.cer -pfx P.pfx


(3)导入证书


把TempCA.cer导入W,Y,P的受信任的根证书区,把各自的pfx导入各自的个人证书区,这样每台机器都有2个证书,一个根证书,一个自己的证书。


(4)创建WCF


首先在P上创建WCF服务,再在Y上创建WCF服务并引入P的服务,再在W上创建客户端引入Y的服务。


(5)配置安全选项


W上的配置类似于:





   
<system.serviceModel>
       
<bindings>
           
<wsHttpBinding>
               
<binding name="ServerBinding">
                   
<security mode="Message">
                       
<message clientCredentialType="Certificate" negotiateServiceCredential="true" algorithmSuite="Default" establishSecurityContext="true"/>
                   
</security>
               
</binding>
           
</wsHttpBinding>
       
</bindings>
       
<client>
           
<endpoint address="http://Y/Test.svc" binding="wsHttpBinding" bindingConfiguration="ServerBinding" contract="WcfClients.ITest" name="WSHttpBinding_ITest" behaviorConfiguration="TestEndpointBehavior">
               
<identity>
                   
<dns value="Y"/>
               
</identity>
           
</endpoint>
       
</client>
       
<behaviors>
           
<endpointBehaviors>
               
<behavior name="TestEndpointBehavior">
                   
<clientCredentials>
                       
<clientCertificate findValue="W" storeLocation="LocalMachine" x509FindType="FindBySubjectName" />
                       
<serviceCertificate>
                           
<authentication certificateValidationMode="ChainTrust" revocationMode="NoCheck" />
                       
</serviceCertificate>
                   
</clientCredentials>
               
</behavior>
           
</endpointBehaviors>
       
</behaviors>
   
</system.serviceModel>



简单解释一下:


binding节配置了消息的传输方式和安全策略,并配置有name,在后面的endpoint里可以引用。服务端和客户端的binding要一致。


client节点代表客户端引用了哪些endpoint,每个endpoint的binding是什么。


endpointBehavior节点是一种扩展,针对本例,只需要配置客户端需要的具体身份凭据。服务端和客户端都可以定义
endpointBehavior,它是针对endpoint的。另外还有针对service的serviceBehavior,它只需配置在服务端,客
户端不需要。


==========================================================


Y的配置:





   
<system.serviceModel>
       
<client>
           
<endpoint address="http://P/Service1.svc" binding="wsHttpBinding" bindingConfiguration="ServerBinding" contract="IService1" name="WSHttpBinding_IService1" behaviorConfiguration="ServiceBehavior">
               
<identity>
                   
<dns value="P" />
               
</identity>
           
</endpoint>
       
</client>
       
<bindings>
           
<wsHttpBinding>
               
<binding name="ServerBinding">
                   
<security mode="Message">
                       
<message clientCredentialType="Certificate" />
                   
</security>
               
</binding>
           
</wsHttpBinding>
       
</bindings>
       
<services>
           
<service behaviorConfiguration="WcfSample.TestBehavior" name="WcfSample.Test">
               
<endpoint address="" binding="wsHttpBinding" contract="WcfSample.ITest" bindingConfiguration="ServerBinding">
               
</endpoint>
               
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
           
</service>
       
</services>
       
<behaviors>
           
<serviceBehaviors>
               
<behavior name="WcfSample.TestSecureBehavior">
                   
<serviceMetadata httpGetEnabled="true"/>
                   
<serviceDebug includeExceptionDetailInFaults="false"/>
                   
<serviceCredentials>
                       
<serviceCertificate storeName="My"
                        storeLocation
="LocalMachine"
                        x509FindType
="FindBySubjectName"
                        findValue
="TomYang-PC"/>
                       
<clientCertificate>
                           
<authentication certificateValidationMode="ChainTrust" revocationMode="NoCheck" />
                       
</clientCertificate>
                   
</serviceCredentials>
               
</behavior>
           
</serviceBehaviors>
           
<endpointBehaviors>
               
<behavior name="ServiceBehavior">
                   
<clientCredentials>
                       
<clientCertificate storeName="My"
                        storeLocation
="LocalMachine"
                        x509FindType
="FindBySubjectName"
                        findValue
="Y"/>
                       
<serviceCertificate>
                           
<authentication certificateValidationMode="ChainTrust" revocationMode="NoCheck"/>
                       
</serviceCertificate>
                   
</clientCredentials>
               
</behavior>
           
</endpointBehaviors>
       
</behaviors>
   
</system.serviceModel>



Y的配置之所以如此之长,是因为它既要配置服务端,又要配置客户端。其中的service节和serviceBehavior节是与W的对应之处,也就是服务端需要什么样的凭据,客户端就要提供,反过来,客户端需要服务端提供什么样的凭据,服务端也要提供相应的配置。


==========================================================


P的配置:





   
<system.serviceModel>
       
<bindings>
           
<wsHttpBinding>
               
<binding name="ServerBinding">
                   
<security>
                       
<message clientCredentialType="Certificate" />
                   
</security>
               
</binding>
           
</wsHttpBinding>
       
</bindings>
       
<services>
           
<service name="Service1" behaviorConfiguration="Service1Behavior">
               
<endpoint address="" binding="wsHttpBinding" contract="IService1" bindingConfiguration="ServerBinding">
               
</endpoint>
               
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
           
</service>
       
</services>
       
<behaviors>
           
<serviceBehaviors>
               
<behavior name="Service1Behavior">
                   
<serviceMetadata httpGetEnabled="true" />
                   
<serviceDebug includeExceptionDetailInFaults="true" />
                   
<serviceCredentials>
                       
<clientCertificate>
                           
<authentication certificateValidationMode="ChainTrust" revocationMode="NoCheck" />
                       
</clientCertificate>
                       
<serviceCertificate findValue="P" storeName="My" x509FindType="FindBySubjectName" />
                   
</serviceCredentials>
               
</behavior>
           
</serviceBehaviors>
       
</behaviors>
   
</system.serviceModel>


OK, 所有代码与配置均写好了,还有要注意的一点是防火墙的问题,假如你所做的WCF使用了被防火墙屏蔽的端口时,你需要配置一下防火墙,方法可以是添加例外的
应用程序,或者添加端口。还有元数据的配置(即address为mex的那些东东),在正式的生产环境中是需要去掉的,它们可能会泄露系统敏感信息。


四、剩余的工作


这个Case只做到了安全通信,没有实现更详细的控制,比如身份识别(服务端收到请求后如何知道请求者是谁),权限控制(如何把一个用户映射到角
色,某个功能只对某些用户名角色开放)。身份识别可以使用证书的thumbprint,权限控制可以用代码访问安全性设置。这方面还没深入了解,有谁有这
方面的经验可以分享啊?


对于有些应用,比如不需要太详细的控制,只要求实现两点间的安全通信即可,那么这个Case已经够用了。

posted on 2012-09-21 10:41  jerry liu  阅读(358)  评论(0编辑  收藏  举报