一个服务的客户端认证是通过向服务提供一系列信赖的声明。声明可以是任何形式,只要客户端和服务端理解这个格式并信赖它的来源就可以。
如果客户端和服务端共享一个秘密信息,比如一个用户名和密码,只要客户端通过一个合法认证发送数据,服务端就相信客户端所说的。这是HTTP基本认证架构。在一个客户端机器和服务器运行在活动目录或者域中定义的账户的Windows-Only环境中,客户端和服务端已经是一种信赖关系。在这种情况下,可以使用Windows 认证,而Kerbeors或者NTLM令牌将会使用。如果客户端和服务端都信赖一些第三方组织而且不是一个Windows域的一部分时,基于证书的认证时最适合的,这样客户端可以从源获取一个服务端信任的证书并发送给服务端。
当安全模式设置为Transport时,一个服务端在transport元素的clientCredentialType属性中确定客户端认证要求。这是通过在服务端的服务描述中的绑定配置中完成的,可以在配置文件中或者代码中定义。不同的绑定有不同的客户端认证元数据。表8.1简要列出了内建绑定的选项。
表8.1 传输安全的客户端认证
当使用基于传输安全的客户端认证时,客户端必须在发送消息前向信道附加声明。客户端必须附加符合服务要求的声明。例如,如果基本认证需要一个基于HTTP的绑定,客户端必须发送一个用户名和密码。如果有任何绑定需要证书认证,客户端必须使用它的私钥对消息签名并从一个由服务端信赖的权威组织发送一个数字证书(如果服务端还没有数字证书)。
使用基本认证和basicHttpBinding的认证过程
列表8.4, “使用basicHttpBinding加密”,描述了一个使用basicHttpBinding传输安全模式来通过SSL实现加密的服务配置文件。为了向这个例子中添加用户名/密码,需要将clientCredentialType属性改为Basic.列表8.6显示了一个实现的一个要求在传输层认证的改变的配置文件的片段。这个服务将很适合互联网通信因为认证通过一个安全传输层传输。
列表8.6 使用basicHttpBinding的基本认证
<basicHttpBinding> <binding name="MyBinding"> <security mode="Transport"> <transport clientCredentialType="Basic"> <extendedProtectionPolicy policyEnforcement="Never" /> </transport> </security> </binding> </basicHttpBinding>
当使用基本认证时,客户端必须向服务端传递一个用户名和密码。这个使用一个代理类或者直接在信道上完成的。列表8.7显示了终结点使用basicHttpBinding和基本认证向服务端传输证书的客户端代码。
列表8.7 从一个客户端传递用户名和密码
proxy.ClientCredentials.UserName.UserName = "MyDomain\\Me";
proxy.ClientCredentials.UserName.Password = "SecretPassword";
当安全风险不是很大时且在一个客户端和一个服务端共享一条私有消息是可行的时候使用基本认证模式或者用户名/密码认证模式是合适的。因为密码很容易存储在人们桌面上的即时日志中,数据库表里或者配置文件中,所以它们很容易被拷贝或者不被注意的情况下被查看。为了保持它们”新鲜”,它们频繁的无效(“你的密码将会在10天内过期”)所以有额外的开销涉及进来。额外的,因为人们常常在多个账户中重用同一个密码,一个账户的危机可能导致其他系统危机。
使用Windows 证书认证
其他的认证方案比用户名/密码方式更加更全。如果你在一个有活动目录的Window 环境中工作,可以使用Windows认证。这需要客户端的用户/进程的身份信息并向服务端发送这些证明信息。这是一个单点登录解决方案,在这个方案中档用户登录到Windows域中后,用户的证书可以可以自动的从客户端机器传输到服务端。当使用Windows认证时,列表8.7显示的客户端代码就不需要了。列表8.8显示了使用Windows认证的net.tcp绑定。
列表8.8 使用basicHttpBinding的Windows认证
<basicHttpBinding> <binding name="MyBinding"> <security mode="Transport"> <transport clientCredentialType="Windows"> <extendedProtectionPolicy policyEnforcement="Never" /> </transport> </security> </binding> </basicHttpBinding>
使用证书和netTcpBinding认证
数字证书相比密码可以提供一个更加广泛形式的验证。对要求安全的场景来说,快,基于证书的通信,netTcpBinding是一个好的选择。可以在复杂局域网中找到证书与混合安全模式一起工作,包括Windows, UNIX和第三方的LDAP 认证。在互联网上,如果你需要快速的,安全的服务到服务的通信,而且你可以确定哪个防火墙端口打开了,netTcpBinding可以证明是非常有价值的。使用基于证书的NetTcpBinding连接了快速通信和鲁棒性安全。
列表8.9 显示了一个使用传输层安全的服务配置和一个基于证书的客户端认证。有很多点是没有价值的。首先,服务通过在NetTcpBinding绑定中使用clientCredentialType来配置为需要客户端证书。其次,服务的证书在<serviceCredential>节点中确定。这是必要的以便于服务端知道在SSL握手中使用哪个证书和密钥。第三,服务端通过确定PeerTrust为certificationValidationMode配置去忽略验证客户端证书的路径。当与通过MakeCert.exe生成的证书一起工作时这是必须的,而不是获取的真实证书或者从一个可信赖组织生成的。
列表8.9 使用NetTcpBinding的证书认证
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <bindings> <netTcpBinding> <binding name="MyBinding"> <security> <transport clientCredentialType="Certificate"> <extendedProtectionPolicy policyEnforcement="Never" /> </transport> <message clientCredentialType="None" /> </security> </binding> </netTcpBinding> </bindings> <behaviors> <serviceBehaviors> <behavior name="metadata"> <serviceMetadata httpGetEnabled="true" /> </behavior> <behavior name="NewBehavior"> <serviceCredentials> <clientCertificate> <authentication certificateValidationMode="PeerTrust" /> </clientCertificate> <serviceCertificate findValue="localhost" x509FindType="FindBySubjectName" /> </serviceCredentials> </behavior> </serviceBehaviors> </behaviors> <services> <service behaviorConfiguration="metadata" name="EssentialWCF.EmployeeInformation"> <endpoint address="" binding="netTcpBinding" bindingConfiguration="MyBinding" contract="EssentialWCF.IEmployeeInformation" /> <endpoint address="mex" binding="mexHttpBinding" bindingConfiguration="" contract="IMetadataExchange" /> <host> <baseAddresses> <add baseAddress="http://localhost:8000/EffectiveWCF" /> <add baseAddress="net.tcp://localhost:8001/EffectiveWCF" /> </baseAddresses> </host> </service> </services> </system.serviceModel> </configuration>
为了在客户端和服务端之间初始化通信,客户端必须确定一个用来认证的证书。这可以在配置文件中或者代码中完成。列表8.10显示了为在认证中使用的服务附加一个证书到信道的客户端代码。在对等信赖下,服务将在它的可信赖人文件夹中寻找证书。如果找到了,访问将会被允许;如果找不到,访问将会被拒绝。
列表8.10 证书认证的客户端代码
Employee employee = new Employee(358, "Daniel", "Dong", "210"); EmployeeInformationClient proxy = new EmployeeInformationClient(); proxy.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.PeerTrust; proxy.ClientCredentials.ClientCertificate.SetCertificate( StoreLocation.CurrentUser, StoreName.My, X509FindType.FindBySubjectName, "MyClientCert"); try { Employee e = proxy.UpdateEmployee(employee); Console.WriteLine("Name is: {0}", e.FirstName); } catch (Exception ex) { Console.WriteLine(ex.Message); }