[WCF安全3]使用wsHttpBinding构建基于SSL与UserName授权的WCF应用程序

      上一篇文章中介绍了如何使用wsHttpBinding构建UserName授权的WCF应用程序,本文将为您介绍如何使用wsHttpBinding构建基于SSL的UserName安全授权的WCF应用程序。

  与上篇文章一样,同样将该示例分为服务端与客户端介绍。

  1. 服务端

  (1) 实现CustomUserNameValidator

  首先实现CustomUserNameValidator,如何创建CustomUserNameValidator请参见第一篇第二篇文章。

 1 public class CustomUserNameValidator : UserNamePasswordValidator
 2 {
 3   private const string USERNAME_ELEMENT_NAME = "userName";
 4 
 5    private const string PASSWORD_ELEMENT_NAME = "password";
 6 
 7    private const string FAULT_EXCEPTION_MESSAGE = "UserName or Password is incorrect!";
 8 
 9    public override void Validate(string userName, string password)
10    {
11      Guarder.Guard.ArgumentNotNull(userName)
12                 .ArgumentNotNull(password);
13       var validateUserName = ConfigurationManager.AppSettings[USERNAME_ELEMENT_NAME];
14       var validatePassword = ConfigurationManager.AppSettings[PASSWORD_ELEMENT_NAME];
15       var validateCondition = userName.Equals(validateUserName) && password.Equals(validatePassword);
16       if (!validateCondition)
17       {
18         throw new FaultException(FAULT_EXCEPTION_MESSAGE);
19       }
20   }
21 }

  (2) 注册服务端证书

  与上篇文章中一样,首先需要向currentUser或localMachine中注册一个X509证书。

1 makecert.exe -sr CurrentUser -ss My -a sha1 -n CN=ServerCert -sky exchange –pe

  (3) 完成服务端配置文件

 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <configuration>
 3   <startup>
 4     <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
 5   </startup>
 6   <appSettings>
 7     <add key="username" value="username"/>
 8     <add key="password" value="password"/>
 9   </appSettings>
10   <system.serviceModel>
11     <behaviors>
12       <serviceBehaviors>
13         <behavior name="securityBehavior">
14           <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
15           <serviceDebug includeExceptionDetailInFaults="true" />
16           <serviceCredentials>
18             <serviceCertificate
19               findValue="ServerCert"
20               x509FindType="FindBySubjectName"
21               storeLocation="CurrentUser"
22               storeName="My"/>
23             <userNameAuthentication
24               userNamePasswordValidationMode="Custom"
25               customUserNamePasswordValidatorType="TimeSynchronizeHttpsServer.CustomUserNameValidator,TimeSynchronizeHttpsServer"/>
26           </serviceCredentials>
27         </behavior>
28       </serviceBehaviors>
29     </behaviors>
30     <bindings>
31       <wsHttpBinding>
32         <binding name="securityMessageBinding">
33           <security mode="TransportWithMessageCredential">
34             <transport clientCredentialType="Basic"/>
35             <message clientCredentialType="UserName"/>
36           </security>
37         </binding>
38       </wsHttpBinding>
39     </bindings>
40     <services>
41       <service name="TimeSynchronizeHttpsServer.TimeSynchronizeService" behaviorConfiguration="securityBehavior">
44         <endpoint address="https://127.0.0.1:12218/TimeSynchronize"
45                   binding="wsHttpBinding"  bindingConfiguration="securityMessageBinding"
46                   contract="TimeSynchronizeHttpsServer.ITimeSynchronizeService">
47           <identity>
48             <dns value="localhost" />
49           </identity>
50         </endpoint>
51         <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
52         <host>
53           <baseAddresses>
54             <add baseAddress="http://localhost:8733/Design_Time_Addresses/TimeSynchonizeHttpsServer/TimeSynchronizeService/" />
55           </baseAddresses>
56         </host>
57       </service>
58     </services>
59   </system.serviceModel>
60 </configuration>

  其中serviceCredentials节点中添加了serviceCertificate节点,该节点用来指定使用的证书。findValue,此attribute指定的是(2)中命令行中CN=右侧的字符。userNameAuthentication制定了自定义的Validator。binding节点中使用了wsHttpBinding,并将传输的加密方式指定为TransportWithMessageCredential,并制定Message的授权方式为UserName,Transport的授权方式为Basic。endpoint的地址需要使用https协议作为基地址的头部。

  (4) 将服务端应用程序绑定到SSL证书上

  如果你的WCF的承载方式为self-host(所谓self-host方式,指将WCF服务host在一个windows外壳中,包括winform、wpf、windowService。这种方式是相对于IIS承载来说的,IIS承载方式网上介绍的例子已经很多了,这篇文章主要介绍self-host方式。),需要手动将你的外壳程序绑定到SSL证书上。

  首先需要使用(2)中的方式在LocalMachine中建立一个证书,注意,一定是LocalMachine中。然后,使用下面的命令行代码,将你的服务端外壳程序绑定到SSL证书上。

1 netsh http add sslcert ipport=0.0.0.0:12218 certhash=1dca86867481b22c8f15a134df62af649cc3343a clientcertnegotiation=enable appid={02639d71-0935-35e8-9d1b-9dd1a2a34627}

  其中,ipport的值中的0.0.0.0代表本地地址,冒号后面代表端口;certhash的值为证书的thumbPrint值,怎么找到这个值呢?follow this article吧,哈哈,英文的,z正好提高一下你的英语水平,哈哈。appid中的值是程序的guid,在项目属性的assemblyInfo中可以找到。

  2. 客户端

  (1) 实现CertificateValidator

  这个是在第二篇文章中没有的,这个Validator干啥用的呢。这个东西是因为我们的证书都是自己颁发给给自己的,所以在安全性上存在一些问题,如果我们用根证书颁发机构颁发的证书,让这个Validator见鬼去吧,哈哈。

 1 class CertificateValidator
 2 {
 3   public static void SetCertificatePolicy ( )
 4   {
 5     ServicePointManager.ServerCertificateValidationCallback
 6                        += RemoteCertificateValidate;
 7   }
 8   private static bool RemoteCertificateValidate ( object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors error )
 9   {
10     System.Console.WriteLine ( "Warning, trust any certificate" );
11      return true;
12   }
13 }

  在客户端调用代码时,调用客户端的Contract之前,调用CertificateValidator.SetCertificatePolicy()方法。

  (2) 完成客户端配置文件

 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <configuration>
 3   <startup>
 4     <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
 5   </startup>
 6   <appSettings>
 7     <add key="username" value="username"/>
 8     <add key="password" value="password"/>
 9   </appSettings>
10   <system.serviceModel>
11     <bindings>
12       <wsHttpBinding>
13         <binding name="securityMixedBinding">
14           <security mode="TransportWithMessageCredential">
15             <transport clientCredentialType="Basic"/>
16             <message clientCredentialType="UserName"/>
17           </security>
18         </binding>
19       </wsHttpBinding>
20     </bindings>
21     <client>
22       <endpoint address="https://127.0.0.1:12218/TimeSynchronize"
23                 binding="wsHttpBinding" bindingConfiguration="securityMixedBinding"
24                 contract="ITimeSynchronizeService" 
25                 name="DefaultBinding_ITimeSynchronizeService_ITimeSynchronizeService" />
26     </client>
27   </system.serviceModel>
28 </configuration>

  需要注意的是,binding节点需要与服务端配置文件中的binding配置相同。endpoint的地址与service相同,都要采用https基地址。

  (3) 实现客户端调用代码

 1 private const string USERNAME = "userName";
 2 private const string PASSWORD = "password";
 3 
 4 static void Main ( string[] args )
 5 {
 6   var proxy = new TimeSynchronizeServiceClient ( );
 7     var userName = ConfigurationManager.AppSettings[USERNAME];
 8     var password = ConfigurationManager.AppSettings[PASSWORD];
 9     proxy.ClientCredentials.UserName.UserName = userName;
10     proxy.ClientCredentials.UserName.Password = password;
11     CertificateValidator.SetCertificatePolicy ( );
12     var time = proxy.GetTime ( );
13     var builder = new StringBuilder ( );
14     builder.Append ( "Server time is:" ).Append ( " " ).Append ( time );
15     var message = builder.ToString ( );
16     Console.WriteLine ( message );
17     Console.ReadKey ( );
18 }

  在调用客户端的Contract之前,需要调用CertificateValidator.SetCertificatePolicy()方法。

  OK,打完收工。

posted @ 2013-08-26 21:45  yafeya  阅读(413)  评论(0编辑  收藏  举报