摘要:
WCF的安全验证模式有很多种,利用certificate进行验证就是一种常见的手段。本文将介绍如何WCF service 服务器端和WCF client客户端同时安装证书,互相进行验证的情景。
假设:我们已经有两个证书,分别用于WCF服务器端(名为:ServerCerficate)和WCF客户端(名为:ClientCertifate)
问题:如何创建证书?
如果为测试只用,我们可用Microsoft SDK快速创建一个certificate,该command为:makecert.exe
eg. makecert -r -pe -n "CN=WCFServer" -ss My -sky exchange 创建用于服务器端名为WCFServer的证书
eg. makecert -r -pe -n "CN=WCFClient" -ss My -sky exchange 创建用于客户端名为WCFClient的证书
或者到Certificate Authority申请证书。关于如何通过CA申请Certificate,请参考其他Article.
正文:
服务器端代码及配置:
1 首先创建一个简单的WCF Service,由于本文重点是如何配置Certificate,故WCF Service非常简单。
如下:
Contract定义:
public interface IMyMath
{
[OperationContract]
int Sum(int x, int y);
}
ServiceType定义:
public class MyMath:IMyMath
{
public MyMath()
{
Console.WriteLine("MyMath constructor is called");
}
public int Sum(int x, int y)
{
return x + y;
}
}
下面利用ServiceHost来承载WCF Service, 代码如下:
{
static void Main(string[] args)
{
Type serviceType = typeof(MyMath);
ServiceHost host = new ServiceHost(serviceType);
host.Open();
Console.WriteLine("The Service is listenning");
Console.WriteLine("Press <ENTER> to exit");
Console.ReadLine();
host.Close();
}
}
最为关键的是,下面定义一个App.config来对WCF Service的发布。
(创建App.config的方法 :New Item -> app.config -> open with -->选择 svcconfigeditor.exe ,然后开始利用该工具进行编辑)
编辑后的App.config如下:
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="NewBehavior">
<serviceCredentials>
<clientCertificate>
<authentication customCertificateValidatorType="CertificateWCFService.CustomX509CertificatesValidator,CertificateWCFService"
certificateValidationMode="Custom" />
</clientCertificate>
<serviceCertificate findValue="MyServer" storeLocation="CurrentUser"
x509FindType="FindBySubjectName" />
</serviceCredentials>
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<netTcpBinding>
<binding name="CertificateBinding">
<security>
<transport clientCredentialType="Certificate" />
</security>
</binding>
</netTcpBinding>
</bindings>
<services>
<service behaviorConfiguration="NewBehavior" name="CertificateWCFService.MyMath">
<endpoint address="MyMath" binding="netTcpBinding" bindingConfiguration="CertificateBinding"
name="Test" contract="CertificateWCFService.IMyMath" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:8010/service" />
<add baseAddress="http://localhost:8011/service" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>
需要说明的是,在进行配置的时候 最为关键的是<serviceCredentials>的配置,主要包括<serviceCertificate>和<clientCertificate>的配置,
下面对<serviceCertificate>设置作简单介绍:
在服务器端的serviceBehavior,<serviceCetificate>主要用于指定服务器端的certificate. 而clientCertificate用于指定当接受到客户端的证书时,服务器端对客户端的验证行为,比如此时我们可以自定义一个对客户端的证书验证行为,该自定义的验证行为类必须继承自:
System.IdentityModel.Selectors.X509CertificateValidator,并且重写其 void Validate(X509Certificate2 certificate) 方法
如下图所示:
ServiceCertificate的配置
图中FindValue即为安装在服务器端的Certificate ,名为MyServer,该Certificate保存在CurrentUser\My下。
比如在上图中,我们对客户端采用的自定义的证书验证,因此其clientCertificateValidationMode设置为custom,同时我们自定义验证类。
代码如下所示:
{
public override void Validate(X509Certificate2 certificate)
{
try
{
Console.WriteLine(certificate.Thumbprint);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message.ToString());
}
}
}
当然,这个自定义的验证类只是为了测试自用。其效果为:当WCF客户端请求WCF Service时候,Server接受到来自客户端的Request,Server然后验证来自客户端的certificate.此时服务器端自定义的certificate验证器将会运行,从而在服务器端显示出client Certificate的消息摘要。
比如,WCF Client请求时(说明:WCF client客户端代码将在下面给出),服务器端的运行screenshot如下所示:
红色区域即为客户端的certificate 的消息摘要。客户端证书消息摘要如下所示:
可以看到客户端证书的消息摘要被正确的显示在服务器端的验证器中.
说明:通过以上配置,我们已经在服务器端配制好了serviceCertificate和对客户端certificate证书的验证行为。但是以上所做的一切,要work的前提条件是我们在自定义bindingConfiguration时候,必须将MessageClientCredentialType或者TransportClientCredentialType设置为Certificate(如果采用传输安全模式,则为TransportClientCredentialType;如采用MessageClientCredentialType,则为MessageClientCredentialType)。否则以上所有设置都是徒劳,因为缺省的凭证类型是windows credential而非certificate. 设置画面如下图所示:
客户端代码及配置
在WCF Service正确通过host 承载之后,可以同svcutil command从WCF service的httpMetaURL中获得其相应的代理类 。eg svcutil.exe http://localhost:8081/service /out:MyMathProxy.cs /confit:app.config, 则会在客户端自动生成MyMathProxy.cs和App.config,将其加入到WCF 客户端工程中。
注意:
通过svcutil 生成的App.config不能完全胜任需求,我们还需要对其进行简单改写。方法还是通过svcconfigeditor.exe进行编辑。主要需要改写的地方是要指定<EndpointBehavior> --〉<clientCredentials>中的<clientCertificate>和<serviceCertifate>
其中:<clientCertificate>主要指定客户端的certificate
而<serviceCerticate>主要指定当服务器端证书到达客户端时,客户端如何对证书进行验证的行为
客户端的App.config如下:
<configuration>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="NewBehavior">
<clientCredentials>
<clientCertificate findValue="WCFClient" x509FindType="FindBySubjectName" />
<serviceCertificate>
<authentication customCertificateValidatorType="CertificateClient.CustomCertificateValidator,CertificateClient"
certificateValidationMode="Custom" />
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<netTcpBinding>
<binding name="Test" closeTimeout="00:01:00" openTimeout="00:01:00"
receiveTimeout="00:10:00" sendTimeout="00:01:00" transactionFlow="false"
transferMode="Buffered" transactionProtocol="OleTransactions"
hostNameComparisonMode="StrongWildcard" listenBacklog="10"
maxBufferPoolSize="524288" maxBufferSize="65536" maxConnections="10"
maxReceivedMessageSize="65536">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="Transport">
<transport clientCredentialType="Certificate" protectionLevel="EncryptAndSign" />
<message clientCredentialType="Windows" />
</security>
</binding>
</netTcpBinding>
</bindings>
<client>
<endpoint address="net.tcp://localhost:8010/service/MyMath" behaviorConfiguration="NewBehavior"
binding="netTcpBinding" bindingConfiguration="Test" contract="IMyMath"
name="Test">
<identity>
<dns value="MyServer" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
而客户端的自定义证书验证器如下所示:
{
public override void Validate(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate)
{
Console.WriteLine(certificate.Subject);
Console.WriteLine(certificate.Thumbprint);
}
}
WCF client如下所示:
{
static void Main(string[] args)
{
MyMathClient proxy = new MyMathClient();
// proxy.Open();
//int result=proxy.Sum(3, 4);
Console.WriteLine("sum(3,4) is {0}", proxy.Sum(3,4));
Console.WriteLine("sum(3,4) is {0}", proxy.Sum(3, 4));
// proxy.Close();
Console.ReadLine();
}
}
客户端运行screenshot如下所示:
可以看到WCF Client已经验证了WCF Server端的Certificate