从零开始学WCF(14)WCF安全性概述
安全性概述
常见的安全威胁:
1) 观测网络流量以获取敏感信息。以在线银行为列,某个客户端请求将资金从一个账户转账到另一个账户。一个恶意用户截获了此消息(具有账号和密码),随后从盗用的账户将资金转出。
2) 欺诈性实体在客户端未发觉的情况下其服务的作用。在此情况下,恶意用户(欺诈方)充当在线服务,从客户端截获消息以获取敏感消息。然后,欺诈方使用窃取的数据将资金从盗用的账户转出。此类攻击也称为“钓鱼攻击”。
3) 更改消息以获取与调用方所需的结果不同的结果。例如,更改用于存款的账号以便将资金转移到恶意账户。
4) 黑客重放,恶意黑客重放同一采购订单。例如,一家网上书店收到数百张订单并将书籍发送给未订购这些书籍的客户。
5) 使某项服务无法对客户端进行身份验证。在此情况下,服务无法确保相应人员执行该项事务。
传输安全性可提供下列保障:
1) 服务终结点(响应方)身份验证
2) 客户端主体(发起方)身份验证
3) 消息完整性
4) 消息保密性
5) 重放检测
WCF是一个基于SOAP消息的分布式编程平台,因此保护客户端的服务之间的消息安全对于保护数据非常重要。
WCF基于现有安全性基础架构和SOAP消息的经验证的安全标准提供可互操作的安全消息交换通用平台。
与现有的安全性基础结构集成:
通常,Web服务部署都具有现成的安全性解决方案,例如,安全套接字层(SSL)或Kerberos协议。某些服务则利用已部署的安全性基础结构,例如,使用Active Director的WIndows域。当评估和采用新服务时,通常需要与这些现有技术集成。
WCF安全性与现有传输安全模型集成,并且可对基于SOAP消息安全的新传输安全模型使用现有的基础结构。
与现有身份验证模型集成
任何通信安全模型的一个重要组成部分就是能够识别正在通信的实体并对其进行身份验证。这些通信中的实体使用“数字标识”或凭据向通信对等方验证自己的身份。随着分布式通信平台的发展,以实现多种不同的凭据身份验证和相关的安全模型。
1) 在Internet中,通常使用用户名和密码标识用户。
2) 在Intranet中,使用Kerberos域控制器备份用户和服务身份验证变得越来越普遍。
3) 在业务合作伙伴之间,证书可用于对合作伙伴的身份进行相互验证。
因此,在Web服务领域中,同样的服务可向内部企业客户公开,也可想外部合作伙伴或Internet客户公开,重要的是基础结构可提供与这些现有安全身份验证模型的集成。WCF安全性支持多种凭据类型(身份验证模型),其中包括:
1) 匿名调用方。
2) 用户名客户端凭据。
3) 证书客户端凭据。
4) Windows(Kerberos协议和NT LanMan[NTLM])
WCF安全性的功能划分:
1) 传输安全性:
传输安全性包括三项主要安全功能:完整性、保密性和身份验证。完整性就是检测消息是否已被串改的能力。机密性就是保证除预期接收方之外的其他人员都无法读取某个消息;这可以通过加密技术实现。身份验证就是验证已声明标识的能力。将这三项功能结合在一起,有助于确保消息安全地从一个点到达另一个点。
2) 访问控制:
访问控制也成为身份验证。身份验证使得不同的用户可以具有不同的数据查看权限。
3) 审核:
审核就是将安全事件记录到Windows事件日志中。可以记录与安全相关的事件,例如身份验证失败(或成功)。
传输安全性
三项功能——完整性、保密性和身份验证——合称为传输安全:
使用WCF传输安全的常见方案包括:
1) 使用Windows确保传输安全:
WCF客户端和服务部署在Windows域(或WIndows目录林)中。
2) 使用Username和Https确保传输安全:
客户端凭据根据数据库(其中的内容为用户名/密码对)进行身份验证。服务是用受信任的安全套接字层(SSL)证书部署在一个HTTPS地址的。由于消息是通过Internet传输的,因此,客户端和服务需要相互进行身份验证,并且必须在传输过程中保持消息的保密性和完整性。
3) 使用证书确保传输安全:
客户端和服务都具有可用于确保消息安全的证书。客户端和服务通过Internet进行相互通信,执行要求消息完整性、保密性和相互身份验证的重要事务。
传输安全模式
绑定支持的安全模式:
凭据
凭据是一些数据,用于证实已声明表示或功能。出示凭据的操作包括,同事出示数据和对数据的所有权声明。 例如,考虑WCF中支持的两种凭据类型: 1) 用户名 用户名表示已声明的标识,密码表示所有权证明。这种情况下,受信任的颁发机构则是验证用户名和密码的系统。 2) (X.509)证书凭据 在证书凭据中,主题名称、主题备用名称或证书中的特定字段可用于表示已声明标识和/或功能。凭据中的数据所有权证明的建立,是用关联私钥生成签名实现的。传输安全模式(Transport)的凭据类型:
消息安全模式(Message)的凭据类型:
传输安全示例(Transport)DEMO
1) 新建一个WCF Application Service项目,然后定义WCF服务接口:using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;namespace Video14.Demo1.TransportSecurity
{// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "ICalculatorService" in both code and config file together.[ServiceContract]public interface ICalculatorService{[OperationContract]double Add(double n1, double n2);[OperationContract]double Subtract(double n1, double n2);[OperationContract]double Multiply(double n1, double n2);[OperationContract]double Divide(double n1, double n2);}
}
2) 实现该服务类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;namespace Video14.Demo1.TransportSecurity
{// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "CalculatorService" in code, svc and config file together.public class CalculatorService : ICalculatorService{public double Add(double n1, double n2){return n1 + n2;}public double Subtract(double n1, double n2){return n1 - n2;}public double Multiply(double n1, double n2){return n1 * n2;}public double Divide(double n1, double n2){return n1 / n2;}}
}
3) 配置配置文件,在配置文件里指定安全模式为传输安全模式:
<?xml version="1.0"?>
<configuration><system.web><compilation debug="true" targetFramework="4.0" /></system.web><system.serviceModel><services><service name="Video14.Demo1.TransportSecurity.CalculatorService" behaviorConfiguration="CalculatorServiceBehavior"><endpoint address="" binding="wsHttpBinding" bindingConfiguration="Binding1" contract="Video14.Demo1.TransportSecurity.ICalculatorService"/></service></services><bindings><wsHttpBinding><binding name="Binding1"><!--指定在wsHttpBinding基础上使用安全模式为Transport的传输安全模式,所有的安全性都交由传输层来决定,传输层也就是部署方在IIS中进行配置--><security mode="Transport"><!--客户端的凭据类型:客户端无需证明自己的身份--><transport clientCredentialType="None"/></security></binding></wsHttpBinding></bindings><behaviors><serviceBehaviors><behavior name="CalculatorServiceBehavior"><!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment --><serviceMetadata httpGetEnabled="true"/><!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information --><serviceDebug includeExceptionDetailInFaults="false"/></behavior></serviceBehaviors></behaviors><serviceHostingEnvironment multipleSiteBindingsEnabled="true" /></system.serviceModel><system.webServer><modules runAllManagedModulesForAllRequests="true"/></system.webServer></configuration>
4) 编辑生成的bin及服务文件,部署到IIS上。 5) 这里由于使用了Transport模式的传输安全,所有需要打开IIS上支持SSL安全套接字层的支持。打开方法:
6) 我们在IIS上,可以用证书来进行安全套接字层的保障,首先我们需要证书,我们创建一个自签名的证书:
7) 创建好证书后,所有在这台IIS上所部署的服务也好,或者是网站应用程序也好,都可以利用这个证书来进行安全套接字层的部署,所有跟这台IIS上面利用HTTS访问的数据交换都会使用这个证书来加密和签名。证书创建好后,需要在网站上添加对HTTS的支持,并且在HTTPS上指定证书为刚刚创建的TestForService证书,这也就保证了所以使用HTTPS来请求这台上的数据都会使用这个证书来签名和加密。
8) 进一步来看看我们具体的应用程序,需要在应用程序上来指定是否使用SSL:
9) 点击SSL设置后,我们设置客户端证书是“忽略”的:
10) 以上设置完毕后,我们来测试一下,在IE中输入“ https://localhost/TransportSecurity/CalculatorService.svc”来打开这个WCF服务,打开的时候会提示一个警报,这事由于浏览器不认识这个证书,这时候我们点击继续浏览此网站即可。打开后我们可以看到这个WCF服务如下:由于我们没有打开mex的Endpoint,所以我发查看到他的WSDL
11) 如果我们在前面的SSL设置上选中了“要求SSL”,而且是“必需的”
那么我们在IE上开打这个WCF服务的时候就会看到“HTTP 错误 403.7 - Forbidden您尝试访问的页面要求您的浏览器具有该 Web 服务器可识别的安全套接字层(SSL)客户证书”
12) 添加一个客户端程序Client,然后使用SVCUTIL控制台程序生成WCF服务的客户端代理类及配置文件(如果生成的时候有问题请参照:),然后添加到该项目中。在Main方法中进行测试:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Client.ServiceReference1;
using System.Net;
using System.Security.Cryptography.X509Certificates;namespace Client
{class Program{static void Main(string[] args){// WARNING: This code is only needed for test certificates such as those created by makecert. It is // not recommended for production code.//证书策略,需要找到CN=Eric-PC.TYCOFS.COM的证书,就相当于认可这个服务,然后才能进行方法的调用PermissiveCertificatePolicy.Enact("CN=Eric-PC.TYCOFS.COM");// Create a client with given client endpoint configurationCalculatorServiceClient client = new CalculatorServiceClient();// Call the Add service operation.double value1 = 100.00D;double value2 = 15.99D;double result = client.Add(value1, value2);Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);// Call the Subtract service operation.value1 = 145.00D;value2 = 76.54D;result = client.Subtract(value1, value2);Console.WriteLine("Subtract({0},{1}) = {2}", value1, value2, result);// Call the Multiply service operation.value1 = 9.00D;value2 = 81.25D;result = client.Multiply(value1, value2);Console.WriteLine("Multiply({0},{1}) = {2}", value1, value2, result);// Call the Divide service operation.value1 = 22.00D;value2 = 7.00D;result = client.Divide(value1, value2);Console.WriteLine("Divide({0},{1}) = {2}", value1, value2, result);//Closing the client gracefully closes the connection and cleans up resourcesclient.Close();Console.WriteLine();Console.WriteLine("Press <ENTER> to terminate client.");Console.ReadLine();}}// WARNING: This code is only needed for test certificates such as those created by makecert. It is // not recommended for production code.class PermissiveCertificatePolicy{string subjectName;static PermissiveCertificatePolicy currentPolicy;PermissiveCertificatePolicy(string subjectName){this.subjectName = subjectName;ServicePointManager.ServerCertificateValidationCallback +=new System.Net.Security.RemoteCertificateValidationCallback(RemoteCertValidate);}public static void Enact(string subjectName){currentPolicy = new PermissiveCertificatePolicy(subjectName);}bool RemoteCertValidate(object sender, X509Certificate cert, X509Chain chain, System.Net.Security.SslPolicyErrors error){if (cert.Subject == subjectName){return true;}return false;}}
}
13) 运行该客户端程序,运行成功可以获得WCF方法的返回值。
源代码:
消息安全示例(Message)DEMO
1) 新建一个WCF Service Application,新建WCF服务接口类:using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;namespace Video14.Demo2.Windows
{// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "ICalculatorService" in both code and config file together.[ServiceContract(Namespace = "http://Video14.Demo2.Windows")]public interface ICalculatorService{[OperationContract]string GetCallerIdentity();[OperationContract]double Add(double n1, double n2);[OperationContract]double Subtract(double n1, double n2);[OperationContract]double Multiply(double n1, double n2);[OperationContract]double Divide(double n1, double n2);}
}
2) 实现该WCF接口:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;namespace Video14.Demo2.Windows
{// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "CalculatorService" in code, svc and config file together.public class CalculatorService : ICalculatorService{public double Add(double n1, double n2){double result = n1 + n2;return result;}public double Subtract(double n1, double n2){double result = n1 - n2;return result;}public double Multiply(double n1, double n2){double result = n1 * n2;return result;}public double Divide(double n1, double n2){double result = n1 / n2;return result;}public string GetCallerIdentity(){//获取当前调用服务的客户端windows用户名return OperationContext.Current.ServiceSecurityContext.WindowsIdentity.Name;}}
}
3) 配置文件:
<?xml version="1.0"?>
<configuration><system.web><compilation debug="true" targetFramework="4.0" /></system.web><system.serviceModel><services><service name="Video14.Demo2.Windows.CalculatorService" behaviorConfiguration="CalculatorServiceBehavior"><endpoint address="" binding="wsHttpBinding" bindingConfiguration="Binding1" contract="Video14.Demo2.Windows.ICalculatorService"/><endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/></service></services><bindings><wsHttpBinding><binding name="Binding1"><!--SecurityMode设置为Message消息安全模式,并且clientCredentialType客户端身份凭据为windows客户端身份验证--><security mode="Message"><message clientCredentialType="Windows"/></security></binding></wsHttpBinding></bindings><behaviors><serviceBehaviors><behavior name="CalculatorServiceBehavior"><!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment --><serviceMetadata httpGetEnabled="true"/><!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information --><serviceDebug includeExceptionDetailInFaults="false"/></behavior></serviceBehaviors></behaviors><serviceHostingEnvironment multipleSiteBindingsEnabled="true" /></system.serviceModel><system.webServer><modules runAllManagedModulesForAllRequests="true"/></system.webServer></configuration>
4) 部署到IIS上后,添加Client客户端程序测试:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace Client
{class Program{static void Main(string[] args){ServiceReference1.CalculatorServiceClient client = new ServiceReference1.CalculatorServiceClient();// Call the GetCallerIdentity service operationConsole.WriteLine("Caller identity is: {0}", client.GetCallerIdentity());// Call the Add service operation.double value1 = 100.00D;double value2 = 15.99D;double result = client.Add(value1, value2);Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);// Call the Subtract service operation.value1 = 145.00D;value2 = 76.54D;result = client.Subtract(value1, value2);Console.WriteLine("Subtract({0},{1}) = {2}", value1, value2, result);// Call the Multiply service operation.value1 = 9.00D;value2 = 81.25D;result = client.Multiply(value1, value2);Console.WriteLine("Multiply({0},{1}) = {2}", value1, value2, result);// Call the Divide service operation.value1 = 22.00D;value2 = 7.00D;result = client.Divide(value1, value2);Console.WriteLine("Divide({0},{1}) = {2}", value1, value2, result);//Closing the client gracefully closes the connection and cleans up resourcesclient.Close();Console.WriteLine();Console.WriteLine("Press <ENTER> to terminate client.");Console.ReadLine();}}
}
这里获取到了客户端调用的windows用户名后,就可以后续的在服务器端进行校验以及授权的操作了。
源代码:
消息安全示例(Message)DEMO 服务器端有证书的认证
服务器端有一个证书的认证,服务器必须提供证书来保证这个服务是一个有效的服务。这个客户端叫做匿名客户端。 1) WCF服务接口:using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;namespace Video14.Demo3.Anonymous
{// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "ICalculatorService" in both code and config file together.[ServiceContract(Namespace = "http://Video14.Demo3.Anonymous")]public interface ICalculatorService{[OperationContract]bool IsCallerAnonymous();[OperationContract]double Add(double n1, double n2);[OperationContract]double Subtract(double n1, double n2);[OperationContract]double Multiply(double n1, double n2);[OperationContract]double Divide(double n1, double n2);}
}
2) WCF实现类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;namespace Video14.Demo3.Anonymous
{// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "CalculatorService" in code, svc and config file together.public class CalculatorService : ICalculatorService{public bool IsCallerAnonymous(){// ServiceSecurityContext.IsAnonymous returns true if the caller is not authenticated//当前安全上下文的属性来判断当前客户端的请求是否是经过身份证书验证的,如果没有就是匿名的。return ServiceSecurityContext.Current.IsAnonymous; }public double Add(double n1, double n2){double result = n1 + n2;return result;}public double Subtract(double n1, double n2){double result = n1 - n2;return result;}public double Multiply(double n1, double n2){double result = n1 * n2;return result;}public double Divide(double n1, double n2){double result = n1 / n2;return result;}}
}
3) 配置文件:
<?xml version="1.0"?>
<configuration><system.web><compilation debug="true" targetFramework="4.0" /></system.web><system.serviceModel><services><service name="Video14.Demo3.Anonymous.CalculatorService" behaviorConfiguration="CalculatorServiceBehavior"><endpoint address="" binding="wsHttpBinding" bindingConfiguration="Binding1" contract="Video14.Demo3.Anonymous.ICalculatorService"/><endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/></service></services><bindings><wsHttpBinding><binding name="Binding1"><!--安全模式是消息安全模式,并且客户端证书凭据类型为None--><security mode="Message"><message clientCredentialType="None"/></security></binding></wsHttpBinding></bindings><behaviors><serviceBehaviors><behavior name="CalculatorServiceBehavior"><!--指定服务器端的证书凭据,也就是服务器端的身份验证--><serviceCredentials><!--这个服务的凭据是证书,使用证书的凭据来指定该服务进行消息安全的保障。指定证书的证书凭据类型;findValue找到值,storeLocation保存到哪个节点下,storeName就是Personal下面的证书,x509FindType的类型.该证书需要使用证书工具来生成才行,否则我们这个服务器上是没有这个证书的。--><serviceCertificate findValue="localhost" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName"/></serviceCredentials><!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment --><serviceMetadata httpGetEnabled="true"/><!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information --><serviceDebug includeExceptionDetailInFaults="false"/></behavior></serviceBehaviors></behaviors><serviceHostingEnvironment multipleSiteBindingsEnabled="true" /></system.serviceModel><system.webServer><modules runAllManagedModulesForAllRequests="true"/></system.webServer></configuration>
4)编译部署到IIS上。部署完毕后,用IE访问该服务—— http://localhost/Anonymous/CalculatorService.svc 会提示有错误:
Cannot find the X.509 certificate using the following search criteria: StoreName 'My', StoreLocation 'LocalMachine', FindType 'FindBySubjectName', FindValue 'localhost'.
这是由于没有证书导致的。5) 我们来生成证书,使用VS2010的makecert.exe 命令行程序来生成,我们先打开VS Command Prompt(使用管理员身份打开):
具体使用请参考: .aspx 使用此工具生成的只是临时的工具,在开发测试中使用,最终证实部署的证书需要向第三方的颁发机构去购买。 然后再该工具里输入: C:\Windows\system32>makecert.exe -sr LocalMachine -ss MY -a sha1 -n CN=localhost -sky exchange -pe
参数说明:
-sr CurrentUser:指定主题的证书存储位置。Location 可以是 currentuser(默认值)或localmachine
-ss MyTestContainer:指定主题的证书存储名称,输出证书即存储在那里。
-n CN=TestCert:指定主题的证书名称。此名称必须符合 X.500 标准。最简单的方法是在双引号中指定此名称,并加上前缀 CN=;例如,"CN=myName"。
-sky exchange:指定颁发者的密钥类型,必须是 signature、exchange 或一个表示提供程序类型的整数。默认情况下,可传入 1 表示交换密钥,传入 2 表示签名密钥。
-pe:将所生成的私钥标记为可导出。这样可将私钥包括在证书中。
6) 经过以上步骤就生成了证书,那我们在哪里确认查看生成的证书呢:
生成的密钥文件被保存在了我们指定的MyTestContainer中,但到哪去查看我们的证书呢?Windows没有给我们准备好直接的管理证书的入口,但我们可以在MMC控制台自行添加。
- 开始 ? 运行 ? MMC,打开一个空的MMC控制台。
- 在控制台菜单,文件 ? 添加/删除管理单元 ? 添加按钮 ? 选"证书" ? 添加 ? 选"我的用户账户" ? 关闭 ? 确定
- 在控制台菜单,文件 ? 添加/删除管理单元 ? 添加按钮 ? 选"证书" ? 添加 ? 选"计算机账户" ? 关闭 ? 确定
生成的证书在 证书(本地计算机)(也就是参数里的localmachine参数)/个人(参数里的my参数)/证书 里的 “localhost”证书,双击可以查看生成的证书:
7)我们还不能直接使用该证书,这是我们临时生成的,该证书还没有被信任。我们还需要把这个证书放到当前用户的信任节点里,才可以被我们的程序所信任。这时候就需要使用证书管理工具“cergmgr.exe”:
样式为:
certmgr [/add | /del | /put] [options] [/s[/r registryLocation]] [sourceStorename] [/s[/r registryLocation]] [destinationStorename]
具体参数:
参数 参数 | 描述 |
---|---|
sourceStorename | 包含现有证书、CTL 或 要添加、删除、保存或显示的CRL的证书存储区。 这可以是一个存储文件,也可以是一个系统存储。 |
destinationStorename | 输出证书存储区或文件。 |
选项 | 描述 |
---|---|
/add | 将证书、CTL 和 CRL 添加到证书存储区中。 |
/all | 当与 /add 一起使用时添加所有项。 当与 /del 一起使用时删除所有项。 不带 /add 或 /del 选项使用时显示所有项。 /all 选项不能与 /put 一起使用。 |
/c | 当与 /add 一起使用时添加证书。 当与 /del 一起使用时删除证书。 当与 /put 一起使用时保存证书。 不带 /add、/del 或 /put 选项使用时显示证书。 |
/CRL | 当与 /add 一起使用时添加 CRL。 当与 /del 一起使用时删除 CRL。 使用 /put 后,保存 CRL。 不带 /add、/del 或 /put 选项使用时显示 CRL。 |
/CTL | 当与 /add 一起使用时添加 CTL。 当与 /del 一起使用时删除 CTL。 使用 /put 后,保存 CTL。 不带 /add、/del 或 /put 选项使用时显示 CTL。 |
/del | 从证书存储区中删除证书、CTL 和 CRL。 |
/e encodingType | 指定证书编码类型。 默认值为 X509_ASN_ENCODING。 |
/f dwFlags | 指定存储区打开标志。 这是传递到 CertOpenStore 的 dwFlags 参数。 默认值为 CERT_SYSTEM_STORE_CURRENT_USER。 仅当使用 /y 选项时才考虑此选项。 |
/h[elp] | 显示该工具的命令语法和选项。 |
/n nam | 指定要添加、删除或保存的证书的公共名。 此选项只能用于证书,不能用于 CTL 或 CRL。 |
/put | 将证书存储区中的 X.509 证书、CTL 或 CRL 保存到文件。 文件以 X.509 格式保存。 /7 选项可与 /put 选项一起使用以 PKCS #7 格式保存文件。/put 选项后面必须有 /c、/CTL 或 /CRL。 /all 选项不能与 /put 一起使用。 |
/r 位置 | 标识系统存储区的注册表位置。 仅当指定 /s 选项时才考虑此选项。 location 必须是以下项之一:
|
/s | 指示证书存储区是系统存储区。 如果未指定此选项,则会将存储区视为 StoreFile。 |
/sha1 sha1Hash | 指定要添加、删除或保存的证书、CTL 或 CRL 的 SHA1 哈希。 |
/v | 指定详细模式;显示有关证书、CTL 和 CRL 的详细信息。 此选项不能与 /add、/del 或 /put 选项一起使用。 |
/y provider | 指定存储提供程序名称。 |
/7 | 将目标存储区保存为 PKCS #7 对象。 |
/? | 显示该工具的命令语法和选项。 |
可以在MMC里看到已经被拷贝进来:
8) 这时候我们在IE里再次打开WCF服务的时候会出现:
Server Error in '/Anonymous' Application.
密钥集不存在。
的错误信息,这是由于
在证书受信任的前提下,主要是打开浏览权限
解决办法:
Win Xp /2003:在C:\Documents and Settings\All Users\Application Data\Microsoft\Crypto\RSA下为文件夹 MachineKeys 添加Everyone 并赋予浏览权限
Win 7 /Vista /2008: 在C:\ProgramData\Microsoft\Crypto\RSA 目录下为文件夹 MachineKeys 添加Everyone 并赋予浏览权限
即可解决这个错误!
经过以上设置后,再次用IE打开该服务即可正常打开了。
9) WCF服务设置完毕后,添加一个客户端项目来测试一下,需要在客户端项目上添加WCF的引用,客户端Main方法如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Client.ServiceReference1;namespace Client
{class Program{static void Main(string[] args){// Create a client with given client endpoint configurationCalculatorServiceClient client = new CalculatorServiceClient();// Call the GetCallerIdentity operationConsole.WriteLine("IsCallerAnonymous returned: {0}", client.IsCallerAnonymous());// Call the Add service operation.double value1 = 100.00D;double value2 = 15.99D;double result = client.Add(value1, value2);Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);// Call the Subtract service operation.value1 = 145.00D;value2 = 76.54D;result = client.Subtract(value1, value2);Console.WriteLine("Subtract({0},{1}) = {2}", value1, value2, result);// Call the Multiply service operation.value1 = 9.00D;value2 = 81.25D;result = client.Multiply(value1, value2);Console.WriteLine("Multiply({0},{1}) = {2}", value1, value2, result);// Call the Divide service operation.value1 = 22.00D;value2 = 7.00D;result = client.Divide(value1, value2);Console.WriteLine("Divide({0},{1}) = {2}", value1, value2, result);//Closing the client gracefully closes the connection and cleans up resourcesclient.Close();Console.WriteLine();Console.WriteLine("Press <ENTER> to terminate client.");Console.ReadLine();}}
}
10) 修改自动生成的app.config为:
<?xml version="1.0" encoding="utf-8" ?>
<configuration><system.serviceModel><client><endpoint name=""address="http://localhost/Anonymous/CalculatorService.svc"binding="wsHttpBinding"behaviorConfiguration="ClientCredentialsBehavior"bindingConfiguration="Binding1"contract="ServiceReference1.ICalculatorService" /></client><bindings><wsHttpBinding><!-- This configuration defines the security mode as Message and the clientCredentialType as None.--><binding name="Binding1"><security mode = "Message"><message clientCredentialType="None"/></security></binding></wsHttpBinding></bindings><behaviors><endpointBehaviors><behavior name="ClientCredentialsBehavior"><clientCredentials><serviceCertificate><!-- Setting the certificateValidationMode to PeerOrChainTrust means that if the certificate is in the user's Trusted People store, then it will be trusted without performing avalidation of the certificate's issuer chain. This setting is used here for convenience so that the sample can be run without having to have certificates issued by a certificate authority (CA).This setting is less secure than the default, ChainTrust. The security implications of this setting should be carefully considered before using PeerOrChainTrust in production code. --><authentication certificateValidationMode="PeerOrChainTrust" /></serviceCertificate></clientCredentials></behavior></endpointBehaviors></behaviors></system.serviceModel>
</configuration>
11) 运行该服务,可正常获取服务端的数据。
服务器我们承载的服务用我们生成的临时证书来保证消息安全的保证,客户端是使用匿名方式的方法来访问这个服务。
源代码:
从零开始学WCF(14)WCF安全性概述
安全性概述
常见的安全威胁:
1) 观测网络流量以获取敏感信息。以在线银行为列,某个客户端请求将资金从一个账户转账到另一个账户。一个恶意用户截获了此消息(具有账号和密码),随后从盗用的账户将资金转出。
2) 欺诈性实体在客户端未发觉的情况下其服务的作用。在此情况下,恶意用户(欺诈方)充当在线服务,从客户端截获消息以获取敏感消息。然后,欺诈方使用窃取的数据将资金从盗用的账户转出。此类攻击也称为“钓鱼攻击”。
3) 更改消息以获取与调用方所需的结果不同的结果。例如,更改用于存款的账号以便将资金转移到恶意账户。
4) 黑客重放,恶意黑客重放同一采购订单。例如,一家网上书店收到数百张订单并将书籍发送给未订购这些书籍的客户。
5) 使某项服务无法对客户端进行身份验证。在此情况下,服务无法确保相应人员执行该项事务。
传输安全性可提供下列保障:
1) 服务终结点(响应方)身份验证
2) 客户端主体(发起方)身份验证
3) 消息完整性
4) 消息保密性
5) 重放检测
WCF是一个基于SOAP消息的分布式编程平台,因此保护客户端的服务之间的消息安全对于保护数据非常重要。
WCF基于现有安全性基础架构和SOAP消息的经验证的安全标准提供可互操作的安全消息交换通用平台。
与现有的安全性基础结构集成:
通常,Web服务部署都具有现成的安全性解决方案,例如,安全套接字层(SSL)或Kerberos协议。某些服务则利用已部署的安全性基础结构,例如,使用Active Director的WIndows域。当评估和采用新服务时,通常需要与这些现有技术集成。
WCF安全性与现有传输安全模型集成,并且可对基于SOAP消息安全的新传输安全模型使用现有的基础结构。
与现有身份验证模型集成
任何通信安全模型的一个重要组成部分就是能够识别正在通信的实体并对其进行身份验证。这些通信中的实体使用“数字标识”或凭据向通信对等方验证自己的身份。随着分布式通信平台的发展,以实现多种不同的凭据身份验证和相关的安全模型。
1) 在Internet中,通常使用用户名和密码标识用户。
2) 在Intranet中,使用Kerberos域控制器备份用户和服务身份验证变得越来越普遍。
3) 在业务合作伙伴之间,证书可用于对合作伙伴的身份进行相互验证。
因此,在Web服务领域中,同样的服务可向内部企业客户公开,也可想外部合作伙伴或Internet客户公开,重要的是基础结构可提供与这些现有安全身份验证模型的集成。WCF安全性支持多种凭据类型(身份验证模型),其中包括:
1) 匿名调用方。
2) 用户名客户端凭据。
3) 证书客户端凭据。
4) Windows(Kerberos协议和NT LanMan[NTLM])
WCF安全性的功能划分:
1) 传输安全性:
传输安全性包括三项主要安全功能:完整性、保密性和身份验证。完整性就是检测消息是否已被串改的能力。机密性就是保证除预期接收方之外的其他人员都无法读取某个消息;这可以通过加密技术实现。身份验证就是验证已声明标识的能力。将这三项功能结合在一起,有助于确保消息安全地从一个点到达另一个点。
2) 访问控制:
访问控制也成为身份验证。身份验证使得不同的用户可以具有不同的数据查看权限。
3) 审核:
审核就是将安全事件记录到Windows事件日志中。可以记录与安全相关的事件,例如身份验证失败(或成功)。
传输安全性
三项功能——完整性、保密性和身份验证——合称为传输安全:
使用WCF传输安全的常见方案包括:
1) 使用Windows确保传输安全:
WCF客户端和服务部署在Windows域(或WIndows目录林)中。
2) 使用Username和Https确保传输安全:
客户端凭据根据数据库(其中的内容为用户名/密码对)进行身份验证。服务是用受信任的安全套接字层(SSL)证书部署在一个HTTPS地址的。由于消息是通过Internet传输的,因此,客户端和服务需要相互进行身份验证,并且必须在传输过程中保持消息的保密性和完整性。
3) 使用证书确保传输安全:
客户端和服务都具有可用于确保消息安全的证书。客户端和服务通过Internet进行相互通信,执行要求消息完整性、保密性和相互身份验证的重要事务。
传输安全模式
绑定支持的安全模式:
凭据
凭据是一些数据,用于证实已声明表示或功能。出示凭据的操作包括,同事出示数据和对数据的所有权声明。 例如,考虑WCF中支持的两种凭据类型: 1) 用户名 用户名表示已声明的标识,密码表示所有权证明。这种情况下,受信任的颁发机构则是验证用户名和密码的系统。 2) (X.509)证书凭据 在证书凭据中,主题名称、主题备用名称或证书中的特定字段可用于表示已声明标识和/或功能。凭据中的数据所有权证明的建立,是用关联私钥生成签名实现的。传输安全模式(Transport)的凭据类型:
消息安全模式(Message)的凭据类型:
传输安全示例(Transport)DEMO
1) 新建一个WCF Application Service项目,然后定义WCF服务接口:using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;namespace Video14.Demo1.TransportSecurity
{// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "ICalculatorService" in both code and config file together.[ServiceContract]public interface ICalculatorService{[OperationContract]double Add(double n1, double n2);[OperationContract]double Subtract(double n1, double n2);[OperationContract]double Multiply(double n1, double n2);[OperationContract]double Divide(double n1, double n2);}
}
2) 实现该服务类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;namespace Video14.Demo1.TransportSecurity
{// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "CalculatorService" in code, svc and config file together.public class CalculatorService : ICalculatorService{public double Add(double n1, double n2){return n1 + n2;}public double Subtract(double n1, double n2){return n1 - n2;}public double Multiply(double n1, double n2){return n1 * n2;}public double Divide(double n1, double n2){return n1 / n2;}}
}
3) 配置配置文件,在配置文件里指定安全模式为传输安全模式:
<?xml version="1.0"?>
<configuration><system.web><compilation debug="true" targetFramework="4.0" /></system.web><system.serviceModel><services><service name="Video14.Demo1.TransportSecurity.CalculatorService" behaviorConfiguration="CalculatorServiceBehavior"><endpoint address="" binding="wsHttpBinding" bindingConfiguration="Binding1" contract="Video14.Demo1.TransportSecurity.ICalculatorService"/></service></services><bindings><wsHttpBinding><binding name="Binding1"><!--指定在wsHttpBinding基础上使用安全模式为Transport的传输安全模式,所有的安全性都交由传输层来决定,传输层也就是部署方在IIS中进行配置--><security mode="Transport"><!--客户端的凭据类型:客户端无需证明自己的身份--><transport clientCredentialType="None"/></security></binding></wsHttpBinding></bindings><behaviors><serviceBehaviors><behavior name="CalculatorServiceBehavior"><!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment --><serviceMetadata httpGetEnabled="true"/><!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information --><serviceDebug includeExceptionDetailInFaults="false"/></behavior></serviceBehaviors></behaviors><serviceHostingEnvironment multipleSiteBindingsEnabled="true" /></system.serviceModel><system.webServer><modules runAllManagedModulesForAllRequests="true"/></system.webServer></configuration>
4) 编辑生成的bin及服务文件,部署到IIS上。 5) 这里由于使用了Transport模式的传输安全,所有需要打开IIS上支持SSL安全套接字层的支持。打开方法:
6) 我们在IIS上,可以用证书来进行安全套接字层的保障,首先我们需要证书,我们创建一个自签名的证书:
7) 创建好证书后,所有在这台IIS上所部署的服务也好,或者是网站应用程序也好,都可以利用这个证书来进行安全套接字层的部署,所有跟这台IIS上面利用HTTS访问的数据交换都会使用这个证书来加密和签名。证书创建好后,需要在网站上添加对HTTS的支持,并且在HTTPS上指定证书为刚刚创建的TestForService证书,这也就保证了所以使用HTTPS来请求这台上的数据都会使用这个证书来签名和加密。
8) 进一步来看看我们具体的应用程序,需要在应用程序上来指定是否使用SSL:
9) 点击SSL设置后,我们设置客户端证书是“忽略”的:
10) 以上设置完毕后,我们来测试一下,在IE中输入“ https://localhost/TransportSecurity/CalculatorService.svc”来打开这个WCF服务,打开的时候会提示一个警报,这事由于浏览器不认识这个证书,这时候我们点击继续浏览此网站即可。打开后我们可以看到这个WCF服务如下:由于我们没有打开mex的Endpoint,所以我发查看到他的WSDL
11) 如果我们在前面的SSL设置上选中了“要求SSL”,而且是“必需的”
那么我们在IE上开打这个WCF服务的时候就会看到“HTTP 错误 403.7 - Forbidden您尝试访问的页面要求您的浏览器具有该 Web 服务器可识别的安全套接字层(SSL)客户证书”
12) 添加一个客户端程序Client,然后使用SVCUTIL控制台程序生成WCF服务的客户端代理类及配置文件(如果生成的时候有问题请参照:),然后添加到该项目中。在Main方法中进行测试:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Client.ServiceReference1;
using System.Net;
using System.Security.Cryptography.X509Certificates;namespace Client
{class Program{static void Main(string[] args){// WARNING: This code is only needed for test certificates such as those created by makecert. It is // not recommended for production code.//证书策略,需要找到CN=Eric-PC.TYCOFS.COM的证书,就相当于认可这个服务,然后才能进行方法的调用PermissiveCertificatePolicy.Enact("CN=Eric-PC.TYCOFS.COM");// Create a client with given client endpoint configurationCalculatorServiceClient client = new CalculatorServiceClient();// Call the Add service operation.double value1 = 100.00D;double value2 = 15.99D;double result = client.Add(value1, value2);Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);// Call the Subtract service operation.value1 = 145.00D;value2 = 76.54D;result = client.Subtract(value1, value2);Console.WriteLine("Subtract({0},{1}) = {2}", value1, value2, result);// Call the Multiply service operation.value1 = 9.00D;value2 = 81.25D;result = client.Multiply(value1, value2);Console.WriteLine("Multiply({0},{1}) = {2}", value1, value2, result);// Call the Divide service operation.value1 = 22.00D;value2 = 7.00D;result = client.Divide(value1, value2);Console.WriteLine("Divide({0},{1}) = {2}", value1, value2, result);//Closing the client gracefully closes the connection and cleans up resourcesclient.Close();Console.WriteLine();Console.WriteLine("Press <ENTER> to terminate client.");Console.ReadLine();}}// WARNING: This code is only needed for test certificates such as those created by makecert. It is // not recommended for production code.class PermissiveCertificatePolicy{string subjectName;static PermissiveCertificatePolicy currentPolicy;PermissiveCertificatePolicy(string subjectName){this.subjectName = subjectName;ServicePointManager.ServerCertificateValidationCallback +=new System.Net.Security.RemoteCertificateValidationCallback(RemoteCertValidate);}public static void Enact(string subjectName){currentPolicy = new PermissiveCertificatePolicy(subjectName);}bool RemoteCertValidate(object sender, X509Certificate cert, X509Chain chain, System.Net.Security.SslPolicyErrors error){if (cert.Subject == subjectName){return true;}return false;}}
}
13) 运行该客户端程序,运行成功可以获得WCF方法的返回值。
源代码:
消息安全示例(Message)DEMO
1) 新建一个WCF Service Application,新建WCF服务接口类:using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;namespace Video14.Demo2.Windows
{// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "ICalculatorService" in both code and config file together.[ServiceContract(Namespace = "http://Video14.Demo2.Windows")]public interface ICalculatorService{[OperationContract]string GetCallerIdentity();[OperationContract]double Add(double n1, double n2);[OperationContract]double Subtract(double n1, double n2);[OperationContract]double Multiply(double n1, double n2);[OperationContract]double Divide(double n1, double n2);}
}
2) 实现该WCF接口:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;namespace Video14.Demo2.Windows
{// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "CalculatorService" in code, svc and config file together.public class CalculatorService : ICalculatorService{public double Add(double n1, double n2){double result = n1 + n2;return result;}public double Subtract(double n1, double n2){double result = n1 - n2;return result;}public double Multiply(double n1, double n2){double result = n1 * n2;return result;}public double Divide(double n1, double n2){double result = n1 / n2;return result;}public string GetCallerIdentity(){//获取当前调用服务的客户端windows用户名return OperationContext.Current.ServiceSecurityContext.WindowsIdentity.Name;}}
}
3) 配置文件:
<?xml version="1.0"?>
<configuration><system.web><compilation debug="true" targetFramework="4.0" /></system.web><system.serviceModel><services><service name="Video14.Demo2.Windows.CalculatorService" behaviorConfiguration="CalculatorServiceBehavior"><endpoint address="" binding="wsHttpBinding" bindingConfiguration="Binding1" contract="Video14.Demo2.Windows.ICalculatorService"/><endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/></service></services><bindings><wsHttpBinding><binding name="Binding1"><!--SecurityMode设置为Message消息安全模式,并且clientCredentialType客户端身份凭据为windows客户端身份验证--><security mode="Message"><message clientCredentialType="Windows"/></security></binding></wsHttpBinding></bindings><behaviors><serviceBehaviors><behavior name="CalculatorServiceBehavior"><!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment --><serviceMetadata httpGetEnabled="true"/><!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information --><serviceDebug includeExceptionDetailInFaults="false"/></behavior></serviceBehaviors></behaviors><serviceHostingEnvironment multipleSiteBindingsEnabled="true" /></system.serviceModel><system.webServer><modules runAllManagedModulesForAllRequests="true"/></system.webServer></configuration>
4) 部署到IIS上后,添加Client客户端程序测试:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace Client
{class Program{static void Main(string[] args){ServiceReference1.CalculatorServiceClient client = new ServiceReference1.CalculatorServiceClient();// Call the GetCallerIdentity service operationConsole.WriteLine("Caller identity is: {0}", client.GetCallerIdentity());// Call the Add service operation.double value1 = 100.00D;double value2 = 15.99D;double result = client.Add(value1, value2);Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);// Call the Subtract service operation.value1 = 145.00D;value2 = 76.54D;result = client.Subtract(value1, value2);Console.WriteLine("Subtract({0},{1}) = {2}", value1, value2, result);// Call the Multiply service operation.value1 = 9.00D;value2 = 81.25D;result = client.Multiply(value1, value2);Console.WriteLine("Multiply({0},{1}) = {2}", value1, value2, result);// Call the Divide service operation.value1 = 22.00D;value2 = 7.00D;result = client.Divide(value1, value2);Console.WriteLine("Divide({0},{1}) = {2}", value1, value2, result);//Closing the client gracefully closes the connection and cleans up resourcesclient.Close();Console.WriteLine();Console.WriteLine("Press <ENTER> to terminate client.");Console.ReadLine();}}
}
这里获取到了客户端调用的windows用户名后,就可以后续的在服务器端进行校验以及授权的操作了。
源代码:
消息安全示例(Message)DEMO 服务器端有证书的认证
服务器端有一个证书的认证,服务器必须提供证书来保证这个服务是一个有效的服务。这个客户端叫做匿名客户端。 1) WCF服务接口:using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;namespace Video14.Demo3.Anonymous
{// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "ICalculatorService" in both code and config file together.[ServiceContract(Namespace = "http://Video14.Demo3.Anonymous")]public interface ICalculatorService{[OperationContract]bool IsCallerAnonymous();[OperationContract]double Add(double n1, double n2);[OperationContract]double Subtract(double n1, double n2);[OperationContract]double Multiply(double n1, double n2);[OperationContract]double Divide(double n1, double n2);}
}
2) WCF实现类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;namespace Video14.Demo3.Anonymous
{// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "CalculatorService" in code, svc and config file together.public class CalculatorService : ICalculatorService{public bool IsCallerAnonymous(){// ServiceSecurityContext.IsAnonymous returns true if the caller is not authenticated//当前安全上下文的属性来判断当前客户端的请求是否是经过身份证书验证的,如果没有就是匿名的。return ServiceSecurityContext.Current.IsAnonymous; }public double Add(double n1, double n2){double result = n1 + n2;return result;}public double Subtract(double n1, double n2){double result = n1 - n2;return result;}public double Multiply(double n1, double n2){double result = n1 * n2;return result;}public double Divide(double n1, double n2){double result = n1 / n2;return result;}}
}
3) 配置文件:
<?xml version="1.0"?>
<configuration><system.web><compilation debug="true" targetFramework="4.0" /></system.web><system.serviceModel><services><service name="Video14.Demo3.Anonymous.CalculatorService" behaviorConfiguration="CalculatorServiceBehavior"><endpoint address="" binding="wsHttpBinding" bindingConfiguration="Binding1" contract="Video14.Demo3.Anonymous.ICalculatorService"/><endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/></service></services><bindings><wsHttpBinding><binding name="Binding1"><!--安全模式是消息安全模式,并且客户端证书凭据类型为None--><security mode="Message"><message clientCredentialType="None"/></security></binding></wsHttpBinding></bindings><behaviors><serviceBehaviors><behavior name="CalculatorServiceBehavior"><!--指定服务器端的证书凭据,也就是服务器端的身份验证--><serviceCredentials><!--这个服务的凭据是证书,使用证书的凭据来指定该服务进行消息安全的保障。指定证书的证书凭据类型;findValue找到值,storeLocation保存到哪个节点下,storeName就是Personal下面的证书,x509FindType的类型.该证书需要使用证书工具来生成才行,否则我们这个服务器上是没有这个证书的。--><serviceCertificate findValue="localhost" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName"/></serviceCredentials><!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment --><serviceMetadata httpGetEnabled="true"/><!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information --><serviceDebug includeExceptionDetailInFaults="false"/></behavior></serviceBehaviors></behaviors><serviceHostingEnvironment multipleSiteBindingsEnabled="true" /></system.serviceModel><system.webServer><modules runAllManagedModulesForAllRequests="true"/></system.webServer></configuration>
4)编译部署到IIS上。部署完毕后,用IE访问该服务—— http://localhost/Anonymous/CalculatorService.svc 会提示有错误:
Cannot find the X.509 certificate using the following search criteria: StoreName 'My', StoreLocation 'LocalMachine', FindType 'FindBySubjectName', FindValue 'localhost'.
这是由于没有证书导致的。5) 我们来生成证书,使用VS2010的makecert.exe 命令行程序来生成,我们先打开VS Command Prompt(使用管理员身份打开):
具体使用请参考: .aspx 使用此工具生成的只是临时的工具,在开发测试中使用,最终证实部署的证书需要向第三方的颁发机构去购买。 然后再该工具里输入: C:\Windows\system32>makecert.exe -sr LocalMachine -ss MY -a sha1 -n CN=localhost -sky exchange -pe
参数说明:
-sr CurrentUser:指定主题的证书存储位置。Location 可以是 currentuser(默认值)或localmachine
-ss MyTestContainer:指定主题的证书存储名称,输出证书即存储在那里。
-n CN=TestCert:指定主题的证书名称。此名称必须符合 X.500 标准。最简单的方法是在双引号中指定此名称,并加上前缀 CN=;例如,"CN=myName"。
-sky exchange:指定颁发者的密钥类型,必须是 signature、exchange 或一个表示提供程序类型的整数。默认情况下,可传入 1 表示交换密钥,传入 2 表示签名密钥。
-pe:将所生成的私钥标记为可导出。这样可将私钥包括在证书中。
6) 经过以上步骤就生成了证书,那我们在哪里确认查看生成的证书呢:
生成的密钥文件被保存在了我们指定的MyTestContainer中,但到哪去查看我们的证书呢?Windows没有给我们准备好直接的管理证书的入口,但我们可以在MMC控制台自行添加。
- 开始 ? 运行 ? MMC,打开一个空的MMC控制台。
- 在控制台菜单,文件 ? 添加/删除管理单元 ? 添加按钮 ? 选"证书" ? 添加 ? 选"我的用户账户" ? 关闭 ? 确定
- 在控制台菜单,文件 ? 添加/删除管理单元 ? 添加按钮 ? 选"证书" ? 添加 ? 选"计算机账户" ? 关闭 ? 确定
生成的证书在 证书(本地计算机)(也就是参数里的localmachine参数)/个人(参数里的my参数)/证书 里的 “localhost”证书,双击可以查看生成的证书:
7)我们还不能直接使用该证书,这是我们临时生成的,该证书还没有被信任。我们还需要把这个证书放到当前用户的信任节点里,才可以被我们的程序所信任。这时候就需要使用证书管理工具“cergmgr.exe”:
样式为:
certmgr [/add | /del | /put] [options] [/s[/r registryLocation]] [sourceStorename] [/s[/r registryLocation]] [destinationStorename]
具体参数:
参数 参数 | 描述 |
---|---|
sourceStorename | 包含现有证书、CTL 或 要添加、删除、保存或显示的CRL的证书存储区。 这可以是一个存储文件,也可以是一个系统存储。 |
destinationStorename | 输出证书存储区或文件。 |
选项 | 描述 |
---|---|
/add | 将证书、CTL 和 CRL 添加到证书存储区中。 |
/all | 当与 /add 一起使用时添加所有项。 当与 /del 一起使用时删除所有项。 不带 /add 或 /del 选项使用时显示所有项。 /all 选项不能与 /put 一起使用。 |
/c | 当与 /add 一起使用时添加证书。 当与 /del 一起使用时删除证书。 当与 /put 一起使用时保存证书。 不带 /add、/del 或 /put 选项使用时显示证书。 |
/CRL | 当与 /add 一起使用时添加 CRL。 当与 /del 一起使用时删除 CRL。 使用 /put 后,保存 CRL。 不带 /add、/del 或 /put 选项使用时显示 CRL。 |
/CTL | 当与 /add 一起使用时添加 CTL。 当与 /del 一起使用时删除 CTL。 使用 /put 后,保存 CTL。 不带 /add、/del 或 /put 选项使用时显示 CTL。 |
/del | 从证书存储区中删除证书、CTL 和 CRL。 |
/e encodingType | 指定证书编码类型。 默认值为 X509_ASN_ENCODING。 |
/f dwFlags | 指定存储区打开标志。 这是传递到 CertOpenStore 的 dwFlags 参数。 默认值为 CERT_SYSTEM_STORE_CURRENT_USER。 仅当使用 /y 选项时才考虑此选项。 |
/h[elp] | 显示该工具的命令语法和选项。 |
/n nam | 指定要添加、删除或保存的证书的公共名。 此选项只能用于证书,不能用于 CTL 或 CRL。 |
/put | 将证书存储区中的 X.509 证书、CTL 或 CRL 保存到文件。 文件以 X.509 格式保存。 /7 选项可与 /put 选项一起使用以 PKCS #7 格式保存文件。/put 选项后面必须有 /c、/CTL 或 /CRL。 /all 选项不能与 /put 一起使用。 |
/r 位置 | 标识系统存储区的注册表位置。 仅当指定 /s 选项时才考虑此选项。 location 必须是以下项之一:
|
/s | 指示证书存储区是系统存储区。 如果未指定此选项,则会将存储区视为 StoreFile。 |
/sha1 sha1Hash | 指定要添加、删除或保存的证书、CTL 或 CRL 的 SHA1 哈希。 |
/v | 指定详细模式;显示有关证书、CTL 和 CRL 的详细信息。 此选项不能与 /add、/del 或 /put 选项一起使用。 |
/y provider | 指定存储提供程序名称。 |
/7 | 将目标存储区保存为 PKCS #7 对象。 |
/? | 显示该工具的命令语法和选项。 |
可以在MMC里看到已经被拷贝进来:
8) 这时候我们在IE里再次打开WCF服务的时候会出现:
Server Error in '/Anonymous' Application.
密钥集不存在。
的错误信息,这是由于
在证书受信任的前提下,主要是打开浏览权限
解决办法:
Win Xp /2003:在C:\Documents and Settings\All Users\Application Data\Microsoft\Crypto\RSA下为文件夹 MachineKeys 添加Everyone 并赋予浏览权限
Win 7 /Vista /2008: 在C:\ProgramData\Microsoft\Crypto\RSA 目录下为文件夹 MachineKeys 添加Everyone 并赋予浏览权限
即可解决这个错误!
经过以上设置后,再次用IE打开该服务即可正常打开了。
9) WCF服务设置完毕后,添加一个客户端项目来测试一下,需要在客户端项目上添加WCF的引用,客户端Main方法如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Client.ServiceReference1;namespace Client
{class Program{static void Main(string[] args){// Create a client with given client endpoint configurationCalculatorServiceClient client = new CalculatorServiceClient();// Call the GetCallerIdentity operationConsole.WriteLine("IsCallerAnonymous returned: {0}", client.IsCallerAnonymous());// Call the Add service operation.double value1 = 100.00D;double value2 = 15.99D;double result = client.Add(value1, value2);Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);// Call the Subtract service operation.value1 = 145.00D;value2 = 76.54D;result = client.Subtract(value1, value2);Console.WriteLine("Subtract({0},{1}) = {2}", value1, value2, result);// Call the Multiply service operation.value1 = 9.00D;value2 = 81.25D;result = client.Multiply(value1, value2);Console.WriteLine("Multiply({0},{1}) = {2}", value1, value2, result);// Call the Divide service operation.value1 = 22.00D;value2 = 7.00D;result = client.Divide(value1, value2);Console.WriteLine("Divide({0},{1}) = {2}", value1, value2, result);//Closing the client gracefully closes the connection and cleans up resourcesclient.Close();Console.WriteLine();Console.WriteLine("Press <ENTER> to terminate client.");Console.ReadLine();}}
}
10) 修改自动生成的app.config为:
<?xml version="1.0" encoding="utf-8" ?>
<configuration><system.serviceModel><client><endpoint name=""address="http://localhost/Anonymous/CalculatorService.svc"binding="wsHttpBinding"behaviorConfiguration="ClientCredentialsBehavior"bindingConfiguration="Binding1"contract="ServiceReference1.ICalculatorService" /></client><bindings><wsHttpBinding><!-- This configuration defines the security mode as Message and the clientCredentialType as None.--><binding name="Binding1"><security mode = "Message"><message clientCredentialType="None"/></security></binding></wsHttpBinding></bindings><behaviors><endpointBehaviors><behavior name="ClientCredentialsBehavior"><clientCredentials><serviceCertificate><!-- Setting the certificateValidationMode to PeerOrChainTrust means that if the certificate is in the user's Trusted People store, then it will be trusted without performing avalidation of the certificate's issuer chain. This setting is used here for convenience so that the sample can be run without having to have certificates issued by a certificate authority (CA).This setting is less secure than the default, ChainTrust. The security implications of this setting should be carefully considered before using PeerOrChainTrust in production code. --><authentication certificateValidationMode="PeerOrChainTrust" /></serviceCertificate></clientCredentials></behavior></endpointBehaviors></behaviors></system.serviceModel>
</configuration>
11) 运行该服务,可正常获取服务端的数据。
服务器我们承载的服务用我们生成的临时证书来保证消息安全的保证,客户端是使用匿名方式的方法来访问这个服务。
源代码: