基于证书的WCF安全开发详解

前几天成功建立起了一套基于证书的安全的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已经够用了。

与老外讨论WCF

与老外讨论安全通信

原文地址:https://www.cnblogs.com/bighuiwolf/p/1808020.html