WCF作為.net三大組件之一,偉大之處不用多說,但是其加密配置對於我這樣的萌新來說還是頗有難度,因此將幾天來的研究成果共享出來,與各位共勉~
首先聲明我的開發環境,Win10創意者更新 + Visual Studio 2015 update3 + .Net 4.5 + iis10
一、創建X.509證書
1、創建證書
可通過PowerShell或者makecert工具兩種方式,個人建議使用參考資料更多后者,但最新的Windows和VS都不帶makecert,所以需要的話可以到文章結尾處下載。
使用CMD運行:
makecert -sr CurrentUser -ss My -n CN=HelloServiceClient -sky exchange -pe -r
提示Succeded即創建完成。
此時將在當前用戶下的個人項目中看到這個證書,圖中MMC管理單元的使用可以參考這里。
2、設置為信任
由於創建的證書在個人域,且不在信任鏈中,wcf和iis目前不能使用這個證書,一次需要將其設置為信任。
首先先將其導出到磁盤:證書上右鍵--所有任務--導出--選擇導出私鑰--設置私鑰密碼,完成后將得到一個pfx文件。
然后進入上圖的本地計算機,在個人域導入剛才那個pfx文件,完成后雙擊證書,在“證書路徑”標簽中提示“由於CA 根證書不在“受信任的根證書頒發機構”存儲區中,所以它不受信任。”,此時證書仍然不能被使用,我的做法是在本地計算機的“受信任的根證書頒發機構”重復導入一次。此時兩個證書都變成可信,即使將第二次導入的刪除也沒關系。
以上做完沒問題的話,雙擊證書后的狀態應該是這樣的:
二、通過證書加密的項目
1、創建wcf服務
VS中新建“WCF服務應用程序”的項目,命名為WCF_HelloService,此時不用任何修改,已經是可運行的wcf服務,然后將其部署到iis,在瀏覽器中可使用http訪問到服務信息:
並重寫Service1.scv.cs中的GetData()方法:

public string GetData(int value) { if (ServiceSecurityContext.Current != null) { if (!ServiceSecurityContext.Current.IsAnonymous) { return "Hello:" + ServiceSecurityContext.Current.PrimaryIdentity.Name + ";type=" + ServiceSecurityContext.Current.PrimaryIdentity.AuthenticationType; } return "Hello,你輸入的是:" + value; } return "Hello ||未檢測到證書:" + value; }
下面是重點,編輯服務的Web.config文件,使其訪問證書,這里尤其注意要注意用於各項配置互調的名稱設置,如behaviorConfiguration和bindingConfiguration等:

<?xml version="1.0" encoding="utf-8"?> <configuration> <appSettings> <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/> </appSettings> <system.web> <compilation debug="true" targetFramework="4.5.2"/> <httpRuntime targetFramework="4.5.2"/> <httpModules> <add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web"/> </httpModules> </system.web> <system.webServer> <modules runAllManagedModulesForAllRequests="true"> <remove name="ApplicationInsightsWebTracking"/> <add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" preCondition="managedHandler"/> </modules> <!-- 若要在調試過程中瀏覽 Web 應用程序根目錄,請將下面的值設置為 True。 在部署之前將該值設置為 False 可避免泄露 Web 應用程序文件夾信息。 --> <directoryBrowse enabled="true"/> <validation validateIntegratedModeConfiguration="false"/> </system.webServer> <system.serviceModel> <services> <service name="WCF_HelloService.HelloService" behaviorConfiguration="CustomBehavior"> <endpoint binding="mexHttpBinding" contract="IMetadataExchange" address="mex" /> <endpoint address="" binding="wsHttpBinding" contract="WCF_HelloService.IHelloService" bindingConfiguration="CustomBinding"/> </service> </services> <behaviors> <serviceBehaviors> <behavior name="CustomBehavior"> <!-- 為避免泄漏元數據信息,請在部署前將以下值設置為 false 並刪除上面的元數據終結點 --> <serviceMetadata httpGetEnabled="true"/> <!-- 要接收故障異常詳細信息以進行調試,請將以下值設置為 true。在部署前設置為 false 以避免泄漏異常信息 --> <serviceDebug includeExceptionDetailInFaults="false"/> <!--add by Lbh--> <serviceCredentials> <!-- 服務端采用證書詳細配置 findValue :創建證書名稱 storeName:證書儲存詳細位於哪 storeLocation :證書儲存位於當前本機用戶 X509FindType : x509查找證書主題名--> <serviceCertificate findValue="HelloServiceClient" storeName="My" storeLocation="LocalMachine" x509FindType="FindBySubjectName"/> <!--客戶端驗證方式--> <clientCertificate> <authentication certificateValidationMode="None"/> </clientCertificate> </serviceCredentials> </behavior> </serviceBehaviors> </behaviors> <serviceHostingEnvironment multipleSiteBindingsEnabled="true" /> <!--add by Lbh--> <bindings> <wsHttpBinding> <binding name="CustomBinding"> <!--驗證方式--> <security mode="Message"> <message clientCredentialType="Certificate"/> </security> </binding> </wsHttpBinding> </bindings> </system.serviceModel> </configuration>
添加add by 注釋是添加的內容,注意serviceCertificate節點,這里定義了目的證書的信息,請務必使其指向我們剛才配置好的證書,其他諸如命名空間、接口、類名等也應與項目對應。
配置完成后如無問題,刷新剛才的web頁面,我們仍然能看到服務啟動成功的頁面。
2、配置客戶端
隨便添加個winform程序,首先引用上面的服務,然后修改其app.config,同樣需要注意behaviorConfiguration設置:

<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" /> </startup> <system.serviceModel> <bindings> <wsHttpBinding> <binding name="WSHttpBinding_IHelloService"> <security mode="Message"> <transport clientCredentialType="Windows" /> <message clientCredentialType="Certificate" /> </security> </binding> </wsHttpBinding> </bindings> <!--add by Lau--> <behaviors> <endpointBehaviors> <behavior name="CustomBehavior"> <clientCredentials> <clientCertificate findValue="HelloServiceClient" storeName="My" storeLocation="LocalMachine" x509FindType="FindBySubjectName"/> <serviceCertificate> <authentication certificateValidationMode="None"/> </serviceCertificate> </clientCredentials> </behavior> </endpointBehaviors> </behaviors> <client> <endpoint address="http://localhost:8096/HelloService.svc" behaviorConfiguration="CustomBehavior" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IHelloService" contract="HelloService.IHelloService" name="WSHttpBinding_IHelloService"> <identity> <certificate encodedValue="AwAAAAEAAAAUAAAAmIXXyLpHnm+H6oDaCP03aIn03SsgAAAAAQAAABUCAAAwggIRMIIBeqADAgECAhC1V8uCAl/avEkX078G+PlRMA0GCSqGSIb3DQEBBAUAMB0xGzAZBgNVBAMTEkhlbGxvU2VydmljZUNsaWVudDAeFw0xNzA1MDgwNzE1NDBaFw0zOTEyMzEyMzU5NTlaMB0xGzAZBgNVBAMTEkhlbGxvU2VydmljZUNsaWVudDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1+nEnhCxXtfAFxOGgFgzBjcPeO2WmxQI5SC14e6S4yEz+ymJtfKBcEnRSCX7onQDRE5H9dPl9CqoNjI/nkU5OKZ789f5Jh7ISfDK0jfHPa2EYwKK3FwOwGFmx5YY2/7Eb/nmyq6gbroronBIioFU6mcZjkFmTQTDa2WnZJMIsikCAwEAAaNSMFAwTgYDVR0BBEcwRYAQhYkF0TiSQwHAV/0wgMmvE6EfMB0xGzAZBgNVBAMTEkhlbGxvU2VydmljZUNsaWVudIIQtVfLggJf2rxJF9O/Bvj5UTANBgkqhkiG9w0BAQQFAAOBgQA0LvNliWDaWtU4YkqXI8JU9/2mIHO2PK4EVUmUYJu0oxFNEeRcX8ZpAAAA26gRYN+J4IjC1F33NjRG/tzkGJeaTBdOl2SkJo8LqD2D7YfOcMaXfrAsAOcEP5e4z2Z4aZlZp1tOjf0X5SZ6QL4FbPiiJog+1UbF/z5J097peDU7Bw==" /> </identity> </endpoint> </client> </system.serviceModel> </configuration>
與服務端類似地,clientCertificate節點定義了客戶端證書,本例中使用了服務端相同的證書,也可以創建另一個專供客戶端使用。certificate節點的內容來自服務端,引用WCF服務操作完成后會自動生成,如果沒有,請檢查WCF的web.config中是否定義為baseHttpBinding而不是wsHttpBinding(正確的是后者)。
最后在winform加上基本的button和txtResult,並在button按鈕事件寫入代碼:

private void button1_Click(object sender, EventArgs e) { try { HelloService.HelloServiceClient client = new HelloService.HelloServiceClient(); string result = client.GetData(DateTime.Now.Second); txtResult.Text = result; } catch (Exception ex) { this.txtResult.Text = ex.ToString(); } }
運行程序,得到正常結果如圖:
並且通過http攔截到的都是密文:
至此,第一個證書項目完成,demo請到文章結尾處下載。
三、通過證書+帳號密碼加密的項目
1、創建WCF服務
按上面步驟創建好服務,首先添加IdentityModel庫的引用:
然后創建用於校驗的CustomUserPassword類,代碼如下:

using System.IdentityModel.Selectors; using System.ServiceModel; namespace TestUserPassService { public class CustomUserPassword : UserNamePasswordValidator { public override void Validate(string userName, string password) { if (userName != "admin" || password != "admin") { //throw new SecurityNegotiationException("驗證用戶名和密碼時,未通過檢測");// 此異常可能無法被客戶端捕獲 throw new FaultException("用戶名或者密碼錯誤!"); } } } }
最后修改web.config文件,可以看到增加了userNameAuthentication節點,定義的正是自定義的校驗類:

<?xml version="1.0" encoding="utf-8"?> <configuration> <appSettings> <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/> </appSettings> <system.web> <compilation debug="true" targetFramework="4.5.2"/> <httpRuntime targetFramework="4.5.2"/> <httpModules> <add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web"/> </httpModules> </system.web> <system.webServer> <modules runAllManagedModulesForAllRequests="true"> <remove name="ApplicationInsightsWebTracking"/> <add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" preCondition="managedHandler"/> </modules> <!-- 若要在調試過程中瀏覽 Web 應用程序根目錄,請將下面的值設置為 True。 在部署之前將該值設置為 False 可避免泄露 Web 應用程序文件夾信息。 --> <directoryBrowse enabled="true"/> <validation validateIntegratedModeConfiguration="false"/> </system.webServer> <system.serviceModel> <services> <service name="TestUserPassService.Service1" behaviorConfiguration="CustomBehavior"> <endpoint binding="mexHttpBinding" contract="IMetadataExchange" address="mex" /> <endpoint address="" binding="wsHttpBinding" contract="TestUserPassService.IService1" bindingConfiguration="CustomBinding"/> </service> </services> <!--add by Lbh--> <behaviors> <serviceBehaviors> <behavior name="CustomBehavior"> <!-- 為避免泄漏元數據信息,請在部署前將以下值設置為 false --> <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/> <!-- 要接收故障異常詳細信息以進行調試,請將以下值設置為 true。在部署前設置為 false 以避免泄漏異常信息 --> <serviceDebug includeExceptionDetailInFaults="false"/> <serviceCredentials> <!-- 服務端采用證書詳細配置 findValue :創建證書名稱 storeName:證書儲存詳細位於哪 storeLocation :證書儲存位於當前本機用戶 X509FindType : x509查找證書主題名--> <serviceCertificate findValue="HelloServiceClient" storeName="My" storeLocation="LocalMachine" x509FindType="FindBySubjectName"/> <!--客戶端驗證方式--> <clientCertificate> <authentication certificateValidationMode="None"/> </clientCertificate> <userNameAuthentication customUserNamePasswordValidatorType="TestUserPassService.CustomUserPassword,TestUserPassService" userNamePasswordValidationMode="Custom"/> </serviceCredentials> </behavior> </serviceBehaviors> </behaviors> <protocolMapping> <add binding="basicHttpsBinding" scheme="https"/> </protocolMapping> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/> <!--add by Lbh--> <bindings> <wsHttpBinding> <binding name="CustomBinding"> <security mode="Message"> <transport clientCredentialType="Windows"/> <message clientCredentialType="UserName"/> </security> </binding> </wsHttpBinding> </bindings> </system.serviceModel> </configuration>
注意clientCredentialType節點,這里采用映射到Windows賬戶的方式,這是頗為常用和可靠的方式。
部署到iis,沒問題的話,我們仍然可以使用瀏覽器通過http訪問到服務。
2、創建測試客戶端
新建winform客戶端,首先添加引用,修改后的app.config如下:

<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" /> </startup> <system.serviceModel> <bindings> <wsHttpBinding> <binding name="WSHttpBinding_IService1"> <!--add by Lbh--> <security mode="Message"> <transport clientCredentialType="Windows" /> <message clientCredentialType="UserName" /> </security> </binding> </wsHttpBinding> </bindings> <client> <endpoint address="http://localhost:8095/Service1.svc" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService1" contract="Service1.IService1" name="WSHttpBinding_IService1"> <identity> <certificate encodedValue="AwAAAAEAAAAUAAAAmIXXyLpHnm+H6oDaCP03aIn03SsgAAAAAQAAABUCAAAwggIRMIIBeqADAgECAhC1V8uCAl/avEkX078G+PlRMA0GCSqGSIb3DQEBBAUAMB0xGzAZBgNVBAMTEkhlbGxvU2VydmljZUNsaWVudDAeFw0xNzA1MDgwNzE1NDBaFw0zOTEyMzEyMzU5NTlaMB0xGzAZBgNVBAMTEkhlbGxvU2VydmljZUNsaWVudDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1+nEnhCxXtfAFxOGgFgzBjcPeO2WmxQI5SC14e6S4yEz+ymJtfKBcEnRSCX7onQDRE5H9dPl9CqoNjI/nkU5OKZ789f5Jh7ISfDK0jfHPa2EYwKK3FwOwGFmx5YY2/7Eb/nmyq6gbroronBIioFU6mcZjkFmTQTDa2WnZJMIsikCAwEAAaNSMFAwTgYDVR0BBEcwRYAQhYkF0TiSQwHAV/0wgMmvE6EfMB0xGzAZBgNVBAMTEkhlbGxvU2VydmljZUNsaWVudIIQtVfLggJf2rxJF9O/Bvj5UTANBgkqhkiG9w0BAQQFAAOBgQA0LvNliWDaWtU4YkqXI8JU9/2mIHO2PK4EVUmUYJu0oxFNEeRcX8ZpAAAA26gRYN+J4IjC1F33NjRG/tzkGJeaTBdOl2SkJo8LqD2D7YfOcMaXfrAsAOcEP5e4z2Z4aZlZp1tOjf0X5SZ6QL4FbPiiJog+1UbF/z5J097peDU7Bw==" /> </identity> </endpoint> </client> </system.serviceModel> </configuration>
可以看到,配置相比上一個項目簡單許多,因為這里的客戶端無需調用證書,只需定義加密類型。
添加兩個textbox一個button和一個textResult,定義按鈕事件代碼:

using System; using System.Windows.Forms; namespace TestUserPassService_Client { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void textBox1_TextChanged(object sender, EventArgs e) { } private void textBox2_TextChanged(object sender, EventArgs e) { } private void button1_Click(object sender, EventArgs e) { try { Service1.Service1Client client = new Service1.Service1Client(); // 傳入帳號密碼 client.ClientCredentials.UserName.UserName = this.textBox1.Text; client.ClientCredentials.UserName.Password = this.textBox2.Text; string result = client.GetData(DateTime.Now.Second); txtResult.Text = result; } catch (Exception ex) { this.txtResult.Text = ex.ToString(); } } } }
運行客戶端,正確的結果如圖:
假若修改傳入的帳號密碼,結果如下:
查看http傳輸內容,同樣是密文:
至此,本項目完成,demo可在文章結尾處下載。
四、總結
其實wcf加密操作沒有太高深的內容(或者說暫且不用理會里面高深的內容),繁瑣的部分在於web.config和app.config的配置,尤其bindingConfiguration這類名稱命名上,由於網上教程眾多,東拉一塊西扯一塊拼起來是用不了的。比如我這樣的萌新調通兩個項目就花了2天時間,因此這篇文章也盡可能將容易踩到的雷點暴露出來,供后來者們借鑒。當然篇幅和能力有限不能面面俱到,也請各位諒解,有問題可以在下面回復或者請教谷歌。
五、demo下載
--------------------------------------------------------------------------------更新01------------------------------------------------------------------------------------------------------
如果web訪問配置好的服務提示“密鑰集不存在”的問題,請按一下方法處理:
進入路徑:C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys(vista之后可用)
找到剛才創建的證書文件,如果你不確定,可以參考這里
然后右鍵-屬性-安全,保證IIS_IUSRS用戶有讀取該文件的權限(本機測試時IIS是由這個用戶運行的,其他電腦可能會有不同。)即可。