1. Transfer Security
Transfer Security 主要包括三个方面: "消息完整性(Message Integrity)"、"消息机密性 (Message Confidentiality)" 和 "交互验证(Mutual Authentication)"。
消息完整性必须确保消息在传输过程中没有被篡改,接收的消息是完整且正确的;消息机密性必须确保消息不会被任何第三方查阅,消息内容不会泄漏给任何非相关人员;而交互认证则是指客户端和服务器必须通过某种信任机制才能建立正确的连接,同时交互认证还要监测并阻止拒绝服务攻击(DOS)。通常的做法是对消息进行数字签名来确保其完整性,使用非对称加密算法来阻止消息内容外泄,而用户名/密码、X.509 数字证书等方式则可以用来验证对方身份,在这里我们主要讲述如何在WCF中使用用户名/密码的身份验证方式.
2. 创建x.509数字证书:
要使用userName/password方式来验证身份,我们需要为服务器装一个证书,创建证书的作用是因为用户名和密码在client和service传输的过程中需要加密,否则就没有安全性了,x.509rd使用非对称加密加技术.用公钥加密客户端用户名和密码,在服务端用私钥来解密,所以我们得创建这样的证书.使用vs2008的tool中的command命令下执行:makecert -r -pe -n "CN=Temp" -ss My -sky exchange .我们就可以为服务器生成Temp的证书.如下图所示.
点击view可以查看详细信息:
X.509 简单证书介绍:
X.509给出的鉴别框架是一种基于公开密钥体制的鉴别业务密钥管理。一个用户有两把密钥:一把是用户的专用密钥,另一把是其他用户都可利用
的公共密钥。用户可用常规密钥(如DES)为信息加密,然后再用接收者的公共密钥对DES进行加密并将之附于信息之上,这样接收者可用对应的专用密钥
打开DES密锁,并对信息解密。该鉴别框架允许用户将其公开密钥存放在它的目录款项中。一个用户如果想与另一个用户交换秘密信息,就可以直接从对方
的目录款项中获得相应的公开密钥,用于各种安全服务。更多的可以参考MSDN
3.Solution 结构如下图:
solution:WCFValidationContract WCF contract.
WCFValidationClient WCF client端.
WCFValidationServices 主要是实现contract的类
WCFValidationHost..用于启动WCF.
创建服务
[ServiceContract]
public interface IUserName//对应solution中的WCFValidationContract.IUserName
{
[OperationContract]
bool test();
}
public class UserName:IUserName//对应solution中的WCFValidationServices.UserName
{
#region IUserName Members
public bool test()
{
return true;
}
#endregion
}
我们通过继承 UserNamePasswordValidator 来创建一个自定义验证器。//对应solution中的WCFValidationServices.MyValidation
public override void Validate(string userName, string password)
{
//the follow code is testing only.u can read userName and password from DataBase.
if (userName != "user" || password != "pwd")
{
throw new Exception("Unknown Username or Password");
}
}
服务端的配置文件如下:(host的配置文件)
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="UserNameBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceCredentials>
<issuedTokenAuthentication allowUntrustedRsaIssuers="true"></issuedTokenAuthentication>
<clientCertificate>
<authentication certificateValidationMode="None"/>
</clientCertificate>
<serviceCertificate findValue="Temp" storeLocation="CurrentUser" x509FindType="FindBySubjectName"/>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="WCFValidationServices.MyValidation,WCFValidationServices"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="userBinding">
<security mode="Message">
<message clientCredentialType="UserName"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="UserNameBehavior" name="WCFValidationServices.UserName">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="userBinding" name ="username" contract="WCFValidation.IUserName">
</endpoint>
<host>
<baseAddresses>
<add baseAddress="http://localhost/userName"/>
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>
有时候我们在启动服务的时候会生产如下的错误:
这时我们需要使用微软件提供的WCF Samples中的一个工具FindPrivateKey来长到Temp的私钥文件的位置,然后为ASPNET或NET Service用户分配访问权限
WCF Samples下载地址 http://download.csdn.net/source/792492也可以到MSDN上下载
FindPrivateKey的使用介始: http://msdn.microsoft.com/zh-cn/vbasic/aa717039.aspx
4. 创建客户端
启动服务器后,创建客户端代理文件。注意自动生成的客户端配置文件中包含了服务器数字证书的相关信息。
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="username" closeTimeout="00:01:00" openTimeout="00:01:00"
receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false"
transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="Message">
<transport clientCredentialType="Windows" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" negotiateServiceCredential="true"
algorithmSuite="Default" establishSecurityContext="true" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost/userName" binding="wsHttpBinding"
bindingConfiguration="username" contract="IUserName" name="username">
<identity>
<certificate encodedValue="AwAAAAEAAAAUAAAAqxw7daba6mcItJ/tKIAFZfz3TCggAAAAAQAAAPcBAAAwggHzMIIBXKADAgECAhAciN6VY5e8ik4b7Ia5KvftMA0GCSqGSIb3DQEBBAUAMBMxETAPBgNVBAMTCE15U2VydmVyMB4XDTA4MTEyMDA2MTQwNloXDTM5MTIzMTIzNTk1OVowEzERMA8GA1UEAxMITXlTZXJ2ZXIwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALK4gy7ldnVwSjemT3bQjKSEGd/zBjNqYDHf9kwUopwvuHpE287yWD1ytKaYYZf7uEdEtNYKwWeOwSNLEPqxUSW4jF92IqfQwkxa0bQdZQK/Y3TpmseX/hsOtW0FBXV3Ftqq+acrWVkG/J/HFM1eeIyPggVI/QqclrKjBQeikjMdAgMBAAGjSDBGMEQGA1UdAQQ9MDuAEEvEZkpRY3c664NiQiazM3+hFTATMREwDwYDVQQDEwhNeVNlcnZlcoIQHIjelWOXvIpOG+yGuSr37TANBgkqhkiG9w0BAQQFAAOBgQAQLrEU3mnxOlDkKuVx9OatXd0w99I3xMnQOsWvOCITjQrfUeJWz1FOl46pKAXDhJNgfMW133E3ARgUxf+abkJqz9ejhjvzJwx2CJYe843h98fooTPPbSs6rQrfQOxb/KoaxbKoxUaALQsGssEXkN2ImS0jsOUm9aVNnRNWpKMhzA==" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
以配置文件是由svcutil工具生成的.我们在客户端写以下代码来调用服务:
UserNameClient aa = new UserNameClient();
aa.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None;
aa.ClientCredentials.UserName.UserName = "user";
aa.ClientCredentials.UserName.Password = "pwd";
bool flag = aa.test();
Console.WriteLine(flag.ToString());
Console.ReadKey();
这时我们可以在控制应用程序中显示True这个值.
当我们输入一个错误的密码时如:
aa.ClientCredentials.UserName.UserName = "123";
aa.ClientCredentials.UserName.Password = "123";
客户端就会有异常:
这样我们不可以完成了WCF的 userName/password的验证方式,在项目中使用时,我们不必要在每次调用方法的时候设置用户名和密码,我们只要写一个基类就可以了