我的目的是1.能够使用gzipMessageEncoding 2.能够使用 用户名、密码 防止别人调用我的wcf 3.我自己写的程序客户端不用部署证书。
我仿照http://www.cnblogs.com/xiaozhuang/archive/2008/04/30/1177399.html 做了个例子,成功之后,在里面又加入了对消息压缩的部分(微软wcf中的例子中有gzipMessageEncoding),服务器端的配置文件为:
<?xml version="1.0" encoding="utf-8"?> <configuration> <system.web> <compilation debug="false" targetFramework="4.0" /> </system.web> <system.serviceModel> <extensions> <bindingElementExtensions> <add name="gzipMessageEncoding" type="Microsoft.Samples.GZipEncoder.GZipMessageEncodingElement, GZipEncoder, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null" /> </bindingElementExtensions> </extensions> <protocolMapping> <add scheme="http" binding="customBinding" /> </protocolMapping> <bindings> <customBinding> <binding name="UserNameForCertificateBinding"> <gzipMessageEncoding innerMessageEncoding="textMessageEncoding" /> <security authenticationMode ="UserNameForCertificate" messageProtectionOrder="SignBeforeEncrypt" requireDerivedKeys="true" includeTimestamp="true" messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"> </security> <httpTransport authenticationScheme="Anonymous"/> </binding> </customBinding> </bindings> <services> <service behaviorConfiguration="ServiceBehaviorUserName" name="Service"> <endpoint address="" binding="customBinding" contract="IService" bindingConfiguration="UserNameForCertificateBinding"> <identity> <dns value="MyServerCert"/> </identity> </endpoint> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> </service> </services> <behaviors> <serviceBehaviors> <behavior name="ServiceBehaviorUserName" > <serviceMetadata httpGetEnabled="true" /> <serviceCredentials> <serviceCertificate storeLocation="LocalMachine" storeName="My" findValue="CN=MyServerCert" x509FindType="FindBySubjectDistinguishedName"/> <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="MyCustomValidator,App_Code"/> </serviceCredentials> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> <system.webServer> <modules runAllManagedModulesForAllRequests="true"/> </system.webServer> </configuration>
用winform调用客户端
客户端的配置文件为:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <extensions> <bindingElementExtensions> <add name="gzipMessageEncoding" type="Microsoft.Samples.GZipEncoder.GZipMessageEncodingElement, GZipEncoder, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null" /> </bindingElementExtensions> </extensions> <bindings> <customBinding> <binding name="CustomBinding_IService"> <security defaultAlgorithmSuite="Default" authenticationMode="UserNameForCertificate" requireDerivedKeys="true" securityHeaderLayout="Strict" includeTimestamp="true" keyEntropyMode="CombinedEntropy" messageProtectionOrder="SignBeforeEncrypt" messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10" requireSignatureConfirmation="false"> <localClientSettings cacheCookies="false" detectReplays="false" replayCacheSize="900000" maxClockSkew="00:05:00" replayWindow="00:05:00" sessionKeyRenewalInterval="10:00:00" sessionKeyRolloverInterval="00:05:00" reconnectTransportOnFailure="true" timestampValidityDuration="00:05:00" cookieRenewalThresholdPercentage="60" /> <localServiceSettings detectReplays="false" issuedCookieLifetime="10:00:00" maxStatefulNegotiations="128" replayCacheSize="900000" maxClockSkew="00:05:00" negotiationTimeout="00:01:00" replayWindow="00:05:00" inactivityTimeout="00:02:00" sessionKeyRenewalInterval="15:00:00" sessionKeyRolloverInterval="00:05:00" reconnectTransportOnFailure="true" maxPendingSessions="128" maxCachedCookies="1000" timestampValidityDuration="00:05:00" /> </security> <gzipMessageEncoding innerMessageEncoding="textMessageEncoding" /> <httpTransport manualAddressing="false" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" allowCookies="false" authenticationScheme="Anonymous" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" keepAliveEnabled="true" maxBufferSize="65536" proxyAuthenticationScheme="Anonymous" realm="" transferMode="Buffered" unsafeConnectionNtlmAuthentication="false" useDefaultWebProxy="true" /> </binding> </customBinding> <wsHttpBinding> <binding name="WSHttpBinding_IService"> <security> <message clientCredentialType="UserName" /> </security> </binding> </wsHttpBinding> </bindings> <client> <endpoint address="http://localhost:32502/WCFService3/Service.svc" binding="customBinding" bindingConfiguration="CustomBinding_IService" contract="ServiceReference1.IService" name="WSHttpBinding_IService" behaviorConfiguration ="UserNameForCertificateBehaviour"> <identity> <dns value="MyServerCert" /> </identity> </endpoint> </client> <behaviors> <endpointBehaviors> <behavior name="UserNameForCertificateBehaviour"> <clientCredentials> <serviceCertificate> <authentication certificateValidationMode="Custom" customCertificateValidatorType="WindowsFormsApplication3.MyX509Validator,WindowsFormsApplication3" /> <!--<defaultCertificate storeLocation="LocalMachine" storeName="My" findValue="CN=MyServerCert" x509FindType="FindBySubjectDistinguishedName"/> <authentication revocationMode="NoCheck" certificateValidationMode="None"></authentication>--> </serviceCertificate> </clientCredentials> </behavior> </endpointBehaviors> </behaviors> </system.serviceModel> </configuration>
其中客户端的配置文件,如果把注释部分去掉,就可以运行了,但是需要在客户端部署证书,因为是winform程序,如果每个机子,都部署证书,做不到,按照http://www.cnblogs.com/xiaozhuang/archive/2008/04/30/1177399.html中所提到的方法,是可以不用部署证书的。请问我配置文件,如果写,能够做到对wcf进行安全验证,同时也能使用gzipMessageEncoding,并且不用再客户端部署证书! 我自己做的例子,在这里(http://files.cnblogs.com/weiwangyang/WCF.zip)可以下载,以管理员用户,运行
makecert.exe -sr LocalMachine -ss My -a sha1 -n CN=MyServerCert -sky exchange –pe
这个例子就可以在同一台电脑上运行。
我看了园子里写wcf的几位的博客,好像都不能解决这个问题。 多谢!
<identity>
<dns value="MyServerCert" />
</identity>
替换成:
<identity>
<certificate encodedValue="AwAAAAEAAAAU..." />
</identity>
dns查找的话,它会先从dns去查找服务器的证书公钥,也就是 encodedValue 的值。
如果这样做了,我的配置文件,哪些地方是冗余的?多谢指教!
@Mr.Wei: 没啥冗余的,能用就行。
我试了,确实能用。现在有一个疑问,对于winform的程序,这样子,别人打开我的配置文件,找到地址和encodedValue,如果知道UserName和Password 还是可以调用我写的wcf接口,请问除了对UserName和Password 进行加密保护之外,还有什么别的方法没有?
@Mr.Wei: 显示指定服务器证书公钥和指定一个标识来获取服务器证书公钥在安全级别上是一致的。启动安全会话的流程是这样的,客户端通过dns去查询服务器的证书公钥,如果找到,服务器就把证书公钥返回给客户端(在这里,通过抓包,可以清楚的看到服务器返回的公钥明文),客户端再用此公钥加密并签名用户名和密码然后发送给服务器,服务器通过私钥来验证此客户端提供的凭据。如果客户端事先知道服务器证书的公钥,那么就可以省去根据dns查找服务器公钥的步骤,因此公钥是公开的,任何没有经过验证的请求都可以查询此公钥。在PKI体系中,证书公钥本身就是明文公开的,因此不会影响到整个安全体系。
使用消息级别的安全,如果不启用客户端证书,除了服务器无法验明客户端请求是否来自受信源外,其它安全级别和启用了客户端证书是一样的。
提交的用户名和密码是客户端凭据的一部份,消息安全保证客户端凭据在网络间以安全的形式传递(凭据内容加密,同时保证完整性),为了防止第三方通过拦截重发凭据,服务器会通过时间戳的形式来验证两次请求是否为重复请求(WCF配置中有MaxClockSkew,SessionKeyRolloverInterval,TimestampValidityDuration三个配置项来测试重发请求),因此,在此安全模式下需要保密的是客户端凭据(用户名和密码),也就是说一旦用户名和密码泄露,那么安全就无法保障了,因为你没有客户端证书。
在具有客户端证书的安全体系中,也就是完整的PKI中,除了凭据,客户端还能提供一层安全保障就是客户端证书的私钥,由于需要使用私钥签名和加密凭据,那么首先需要读出私钥,为了保证私钥的安全,安全体系会要求发起读取私钥的操作必须先通过验证,通常就是为私钥设置的读取密码。这样,既然你丢失了用户名和密码,丢失了客户端证书,只要第三方无法破解出证书中的私钥或访问私钥的密码,那么第三方仍然无法攻破安全体系。也因为如此,为了保证私钥的安全,一般会将客户端证书安装在第三方介质中,比如USB-KEY,安全装在USB-KEY中的证书只能读出公钥,无法读出私钥(即使你拥有读取私钥的密码),那么当你需要使用私钥的时候,你需要把你的消息传递给USB-KEY,USB-KEY会在内部完成消息的加密和签名然后再返回给你。
总之,你可以把你的用户名和密码简单和网络上传递的其它内容等同起来,只不过前者是用来限制后续会话是否可以执行的标识。更进一步的,WCF中的安全会话还具有更高级别的机制,也就是在你传递凭据到服务器之前,你需要先通过协商来启动一条安全会话通道用以传递凭据,凭据通过验证后会建立一个新的安全会话用以你之后的请求的安全保护。
@Launcher: 多谢了,我大致明白了,以前相当于裸奔,现在这样,相当于穿个小裤衩。
@Launcher: 还是这个问题,上次问的已经解决了,但是在使用过程中发现这样一个问题,如果不使用证书等对wcf进行调用,有一个比较大的函数返回的数据是512k左右,同一个函数,同样的参数,如果加上验证,传输是,网络流量是16M,请问为何差别这么大?有何办法解决这个问题?
@Mr.Wei: 我不知道你是咋测试出来数据为16M的,你可以使用抓包工具抓取请求响应的内容后来观察请求和相应结构的改变。