• 基于证书的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

    与老外讨论安全通信

  • 相关阅读:
    静态成员 执行顺序
    排序之插入排序
    结构体字节对齐问题
    建模基础&UML
    C#中隐藏(new)和方法重载(overide)的区别
    培训记录
    C笔记
    用例
    .NET架构
    C#格式化日期
  • 原文地址:https://www.cnblogs.com/bighuiwolf/p/1808020.html
Copyright © 2020-2023  润新知