zoukankan      html  css  js  c++  java
  • [转贴][WCF Security] 3. X509 身份验证

    原文:http://www.rainsts.net/article.asp?id=474

    个人认为在 Intranet / Internet 环境下,最方便的认证方式应该是 X.509 数字证书。当然,还有一个原因是我用 Windows Authentication 从来没成功过。

    以下我们详细描述如何创建 "Certificate Authentication"。

    1. 创建数字证书

    一般情况下,我们为服务器以及每个客户端都单独创建一个服务器,以便标识其唯一身份。创建数字证书时,必须添加 "-pe" 和 "-sky exchange" 参数。有关数字证书更多的信息,请参考《X.509 & RSA》。

    D:\>makecert -r -pe -n "CN=MyServer" -ss My -sky exchange
    D:\>makecert -r -pe -n "CN=Client1" -ss My -sky exchange



    2. 创建服务

    [ServiceContract]
    public interface IService
    {
      [OperationContract]
      string Test();
    }

    public class MyService : IService
    {
      public string Test()
      {
        Console.WriteLine(ServiceSecurityContext.Current.PrimaryIdentity.AuthenticationType);
        Console.WriteLine(ServiceSecurityContext.Current.PrimaryIdentity.Name);

        return "Server:" + DateTime.Now.ToString();
      }
    }

    public class WcfTest
    {
      public static void Test()
      {
        ServiceHost host = new ServiceHost(typeof(MyService));
        host.Open();
      }
    }



    安全方式:Transport
    客户端验证类型:Certificate

    在 serviceCredentials 中设置好证书的查找参数,同时将验证模式(certificateValidationMode) 设为 None (因为我们创建的是 "不受信任" 的证书)。

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <system.serviceModel>
        <behaviors>
          <serviceBehaviors>
            <behavior name="NewBehavior">
              <serviceMetadata httpGetEnabled="true" httpGetUrl="http://localhost:8080" />
              <serviceDebug includeExceptionDetailInFaults="true" />
              <serviceCredentials>
                <clientCertificate>
                  <authentication certificateValidationMode="None" />
                </clientCertificate>
                <serviceCertificate findValue="MyServer" storeLocation="CurrentUser"
                  x509FindType="FindBySubjectName" />
              </serviceCredentials>
            </behavior>
          </serviceBehaviors>
        </behaviors>
        <bindings>
          <netTcpBinding>
            <binding name="NewBinding0">
               <security mode="Transport">
                <transport clientCredentialType="Certificate" />
              </security>
            </binding>
          </netTcpBinding>
        </bindings>
        <services>
          <service behaviorConfiguration="NewBehavior" name="Learn.Library.WCF.MyService">
            <endpoint address="net.tcp://localhost:8081" binding="netTcpBinding"
              bindingConfiguration="NewBinding0" contract="Learn.Library.WCF.IService">
            </endpoint>
          </service>
        </services>
      </system.serviceModel>
    </configuration>


    3. 创建客户端

    生成代理客户端。

    using (ServiceClient client = new ServiceClient())
    {
      Console.WriteLine(client.Test());
    }


    注意在配置文件中,我们在 EndPointBehaviors 中添加 ClientCredentials 来设置数字整数的相关信息。基于和上面同样的理由,我们也得将 certificateValidationMode 设为 "None"。

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <system.serviceModel>
        <behaviors>
          <endpointBehaviors>
            <behavior name="NewBehavior">
              <clientCredentials>
                <clientCertificate findValue="Client1" x509FindType="FindBySubjectName" />
                <serviceCertificate>
                  <authentication certificateValidationMode="None" />
                </serviceCertificate>
              </clientCredentials>
            </behavior>
          </endpointBehaviors>
        </behaviors>
        <bindings>
          <netTcpBinding>
            <binding name="NetTcpBinding_IService">
              <security mode="Transport">
                <transport clientCredentialType="Certificate" protectionLevel="EncryptAndSign" />
                <message clientCredentialType="Windows" />
              </security>
            </binding>
          </netTcpBinding>
        </bindings>
        <client>
          <endpoint address="net.tcp://localhost:8081/" behaviorConfiguration="NewBehavior"
            binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IService"
            contract="ConsoleApplication1.localhost.IService" name="NetTcpBinding_IService">
            <identity>
              <dns value="MyServer" />
            </identity>
          </endpoint>
        </client>
      </system.serviceModel>
    </configuration>


    运行后,我们对比一下服务器端输出结果和Client1数字证书的信息。

    uploads/200704/29_103351_wcfx509_1.gif


     

    uploads/200704/29_103355_wcfx509_2.gif



    每个客户端的数字证书名称和序号的组合都是唯一的,我们通过它做出相应的验证动作。

    4. 验证器

    WCF 提供了多种数字证书的验证手段,不过我们最习惯的应该还是 "Custom",因为有很多附加行为要处理。

    在服务器端添加一个继承自 X509CertificateValidator 的验证器。

    public class CustomX509CertificateValidator : X509CertificateValidator
    {
      public override void Validate(X509Certificate2 certificate)
      {
        //Console.WriteLine(certificate.Subject);
        //Console.WriteLine(certificate.Thumbprint);

        if (certificate.Thumbprint != "40399ADCC90BB3C4D23D2B639D4356AABDD60091")
          throw new SecurityTokenException("Certificate Validation Error!");
      }
    }


    当然,配置文件也得做些调整,将 certificateValidationMode 验证改为 Custom 和我们自定义的验证类型。这回不用管它是否是 "不信任证书" 了。(客户端配置不做调整)

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <system.serviceModel>
        <behaviors>
          <serviceBehaviors>
            <behavior name="NewBehavior">
              <serviceMetadata httpGetEnabled="true" httpGetUrl="http://localhost:8080" />
              <serviceDebug includeExceptionDetailInFaults="true" />
              <serviceCredentials>
                <clientCertificate>
                  <authentication customCertificateValidatorType=
                    "Learn.Library.WCF.CustomX509CertificateValidator, Learn.Library"
                    certificateValidationMode="Custom" />
                </clientCertificate>
                <serviceCertificate findValue="MyServer" storeLocation="CurrentUser"
                  x509FindType="FindBySubjectName" />
              </serviceCredentials>
            </behavior>
          </serviceBehaviors>
        </behaviors>
        <bindings>
          <netTcpBinding>
            <binding name="NewBinding0">
               <security mode="Transport">
                <transport clientCredentialType="Certificate" />
              </security>
            </binding>
          </netTcpBinding>
        </bindings>
        <services>
          <service behaviorConfiguration="NewBehavior" name="Learn.Library.WCF.MyService">
            <endpoint address="net.tcp://localhost:8081" binding="netTcpBinding"
              bindingConfiguration="NewBinding0" contract="Learn.Library.WCF.IService">
            </endpoint>
          </service>
        </services>
      </system.serviceModel>
    </configuration>


    代码版

    1. 服务器

     
    ServiceHost host = new ServiceHost(typeof(MyService), new Uri("net.tcp://localhost:8081"));

    NetTcpBinding binding = new NetTcpBinding();
    binding.Security.Mode = SecurityMode.Transport;
    binding.Security.Transport.ProtectionLevel = ProtectionLevel.EncryptAndSign;
    binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Certificate;

    host.AddServiceEndpoint(typeof(IService), binding, "");

    host.Credentials.ServiceCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My,
      X509FindType.FindBySubjectName, "MyServer");
    host.Credentials.ClientCertificate.Authentication.CertificateValidationMode =
      X509CertificateValidationMode.None;

    host.Open();


    2. 客户端1

     
    using (ServiceClient client = new ServiceClient())
    {
      client.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My,
        X509FindType.FindBySubjectName, "Client1");
      client.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode =
        X509CertificateValidationMode.None;
      
      Console.WriteLine(client.Test());
    }


    app.config

     
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <system.serviceModel>
        <bindings>
          <netTcpBinding>
            <binding name="NetTcpBinding_IService">
              <security mode="Transport">
                <transport clientCredentialType="Certificate" protectionLevel="EncryptAndSign" />
                <message clientCredentialType="Windows" />
              </security>
            </binding>
          </netTcpBinding>
        </bindings>
        <client>
          <endpoint address="net.tcp://localhost:8081/"
            binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IService"
            contract="ConsoleApplication1.localhost.IService" name="NetTcpBinding_IService">
            <identity>
              <dns value="MyServer" />
            </identity>
          </endpoint>
        </client>
      </system.serviceModel>
    </configuration>


    3. 客户端2

     
    NetTcpBinding binding2 = new NetTcpBinding();
    binding2.Security.Mode = SecurityMode.Transport;
    binding2.Security.Transport.ProtectionLevel = ProtectionLevel.EncryptAndSign;
    binding2.Security.Transport.ClientCredentialType = TcpClientCredentialType.Certificate;

    EndpointAddress endpoint = new EndpointAddress(new Uri("net.tcp://localhost:8081"),
      EndpointIdentity.CreateDnsIdentity("MyServer"));

    ChannelFactory<IService> factory = new ChannelFactory<IService>(binding2, endpoint);

    factory.Credentials.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My,
      X509FindType.FindBySubjectName, "Client1");
    factory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode =
      X509CertificateValidationMode.None;

    IService client = factory.CreateChannel();
    using (client as IDisposable)
    {
      Console.WriteLine(client.Test());
    }


    注意!在不同机器上测试时,注意设置防火墙参数。

    作者:菩提树下的杨过
    出处:http://yjmyzz.cnblogs.com
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    广度优先搜索-八数码问题
    广度优先搜索-鸣人和佐助
    广度优先搜索-迷宫问题
    广度优先搜索-抓住那头牛
    Unity面试题汇总(第一部分)
    独立项目-Socket通讯 应用/客户端和服务器的简单通讯-04
    独立项目-Socket通讯 服务器端代码-04
    独立项目-Socket通讯 客户端代码-03
    独立项目-Socket通讯 发送数据包和接收数据包过程图-02
    独立项目-Socket通讯 服务器端架构图-01
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/1271157.html
Copyright © 2011-2022 走看看