WCF作爲.Net Framework 三大核心應用之一,其在安全性方面也擁有強大的功能,WCF不僅與現有的安全性基礎結構集成,而且還通過使用安全SOAP消息將分佈式安全性擴展到Windows域的範圍之外。
WCF使用綁定的方式可以靈活的配置服務的通信方式、安全策略、交互方式等,系統提供多種綁定方式,比如BasicHttpBinding、WSHttpBinding、NetTcpBinding等。接下將是一個完整的項目實現WCF安全證書認證方式的實現。
一、生成客戶端證書
使用makecert.exe工具生成一個測試用的客戶端證書(這個項目暫不驗證服務端證書,所以只用一個客戶端證書即可),命令如下:
makecert -sr CurrentUser -ss My -n CN=HelloServiceClient -sky exchange -pe -r
生成之後導出爲文件以備後面使用。
二、創建項目
先創建一個解決方案HelloService,添加一個新的WCF服務應用程序WCF_HelloService和一個WPF項目WPF_HelloServiceClient作爲客戶端。
WCF_HelloService中刪除默認生成的服務,添加一個名爲HelloService的WCF服務,服務契約定義如下:
namespace WCF_HelloService
{
// 注意: 使用“重構”菜單上的“重命名”命令,可以同時更改代碼和配置文件中的接口名“IHelloService”。
[ServiceContract]
public interface IHelloService
{
[OperationContract]
string GetHello();
}
}
契約的實現代碼如下:
namespace WCF_HelloService
{
// 注意: 使用“重構”菜單上的“重命名”命令,可以同時更改代碼、svc 和配置文件中的類名“HelloService”。
public class HelloService : IHelloService
{
public string GetHello()
{
if (ServiceSecurityContext.Current != null)
{
if (!ServiceSecurityContext.Current.IsAnonymous)
{
return "Hello:" + ServiceSecurityContext.Current.PrimaryIdentity.Name + ";type=" + ServiceSecurityContext.Current.PrimaryIdentity.AuthenticationType;
}
return "Hello,Anonymous";
}
return "Hello";
}
}
}
三、部署並測試HelloService
打開IIS,添加一個網站HelloService,目錄指向上一不創建的WCF服務應用程序,類型Http,端口8001,修改HelloService應用程序池的.Net Framework版本爲4.0。
先在瀏覽器參看服務是否託管成功,輸入地址 http://charley-pc:8001/HelloService.svc。
提示服務已經託管成功,接下來WPF_HelloServiceClient項目中引用HelloService服務
接下來輸入調用服務的代碼:
private void btn_test_Click(object sender, RoutedEventArgs e)
{
try
{
HelloService.HelloServiceClient client = new HelloService.HelloServiceClient();
string msg = client.GetHello();
SubShowMessage(msg);
}
catch (Exception ex)
{
SubShowMessage(ex.ToString());
}
}
啓動測試程序,單擊Test按鈕,結果如下:
以上步驟就簡單的演示了服務的創建,部署及測試,但由於WCF服務沒有進行任何配置,所以採用的都是默認的配置,由客戶端引用服務自動生成的配置文件app.config內容可以清楚的看到WCF服務默認綁定方式是basicHttpBinding,安全模式爲None,以下是自動生成的配置文件內容:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IHelloService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://charley-pc:8001/HelloService.svc" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IHelloService" contract="HelloService.IHelloService"
name="BasicHttpBinding_IHelloService" />
</client>
</system.serviceModel>
</configuration>
啓動Fiddler2,截獲通信的內容如下:
POST http://charley-pc:8001/HelloService.svc HTTP/1.1
Content-Type: text/xml; charset=utf-8
VsDebuggerCausalityData: uIDPo5Ci8jnFhwFCgKGKh9VCB/cAAAAAJyy1KlXTmUaqjLYQlNh3vE3+ycPqdBxLnxS5MqxaLCwACQAA
SOAPAction: "http://tempuri.org/IHelloService/GetHello"
Host: charley-pc:8001
Content-Length: 133
Expect: 100-continue
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><GetHello xmlns="http://tempuri.org/"/></s:Body></s:Envelope>
響應的數據:
HTTP/1.1 200 OK
Content-Length: 197
Content-Type: text/xml; charset=utf-8
Server: Microsoft-IIS/7.5
X-Powered-By: ASP.NET
Date: Thu, 19 Jul 2012 06:21:48 GMT
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><GetHelloResponse xmlns="http://tempuri.org/"><GetHelloResult>Hello</GetHelloResult></GetHelloResponse></s:Body></s:Envelope>
四、配置WCF服務
由以上數據可以清楚的看到,WCF消息在網絡上都是明文傳輸的,接下來就通過配置WCF服務,使用證書對消息進行加密驗證,WCF提供的安全模式(配置中的Security元素的Mode屬性)除了None,還有Basic、Windows、Digest、Ntlm、Certificate,接下來使用Certificate模式,
1、修改HelloService服務的配置文件如下:
服務端:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="WCF_HelloService.ServiceBehavior">
<!-- 爲避免泄漏元數據信息,請在部署前將以下值設置爲 false 並刪除上面的元數據終結點 -->
<serviceMetadata httpsGetEnabled="true"/>
<!-- 要接收故障異常詳細信息以進行調試,請將以下值設置爲 true。在部署前設置爲 false 以避免泄漏異常信息 -->
<serviceDebug includeExceptionDetailInFaults="false"/>
<serviceCredentials>
<clientCertificate>
<authentication certificateValidationMode="PeerTrust"/>
<certificate x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="TrustedPeople" findValue="HelloServiceClient"/>
</clientCertificate>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="basicBindingConfig">
<security mode="Transport">
<transport clientCredentialType="Certificate"></transport>
</security>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="WCF_HelloService.ServiceBehavior" name="WCF_HelloService.HelloService">
<endpoint binding="basicHttpBinding" bindingConfiguration="basicBindingConfig" contract="WCF_HelloService.IHelloService"></endpoint>
</service>
</services>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
客戶端:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IHelloService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="Transport">
<transport clientCredentialType="Certificate" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="clientBehavior">
<clientCredentials>
<clientCertificate x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="TrustedPeople" findValue="HelloServiceClient"/>
<serviceCertificate>
<authentication certificateValidationMode="None"/>
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint address="http://charley-pc:8001/HelloService.svc" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IHelloService" contract="HelloService.IHelloService"
name="BasicHttpBinding_IHelloService" behaviorConfiguration="clientBehavior" />
</client>
</system.serviceModel>
2、再生成一個證書名稱與站點名稱相同,本例爲:charley-pc,用站點的認證,導出該證書爲文件。
3、運行MMC打開管理控制檯,分別添加一個當前用戶證書管理控制檯和一個本地計算機證書管理控制檯。
在當前用戶下個人證書可以看到剛纔生成的證書 charley-pc ,把它拖到本地計算機個人證書下(當然也可以直接生成證書到本地計算機個人證書下)
4、打開IIS服務器管理,點擊服務證書可以看到證書已經存在了。
5、由於證書驗證需用https傳輸方式,所以需要添加一個https綁定。選擇HelloService站點,點擊“綁定”,然後添加,類型爲https,ssl證書選擇charley-pc。
再次打開瀏覽器輸入地址會出現找不到證書的錯誤,這是由於沒有安裝客戶端證書或證書存儲的位置不正確,這裏需要把客戶端證書放在可信任的人下。
運行客戶端程序,單擊Test按鈕參看結果如下:
由以上結果可以看到已經啓用了安全證書,類型是x509.