[原创]x.509证书在WCF中的应用(CS篇)

为什么要用x.509证书?
WCF的服务端和客户端之间,如果不作任何安全处理(即服务端的<security mode="None">),则所有传输的消息将以明文方式满天飞,在internet/intranet环境下无疑是很不安全的,这就是用证书的目的。(当然WCF还有其它安全机制,比如最常见的UserName方式,但通常每次都要从数据库读取用户名/密码信息进行验证,比较麻烦,开销也大,个人觉得还是证书最为方便)--关于x.509证书

的基本知识,可参见http://www.cnblogs.com/yjmyzz/archive/2008/08/19/1271171.html

大致原理(个人理解,可能不太准确):
正确设置服务端与客户端证书后,WCF的服务端启动时,需要利用服务端证书验证,如果验证通过将正常启动,否则报异常,同时客户端调用服务端方法时,也需要提供客户端证书,服务端接受到客户端证书后,验证客户端证书的有效性,如果通过,则允许客户端正常调用。

 

下面将逐步讲解如何使用:

1.制作证书

先进入到vs2008的命令行状态,即:
开始-->程序-->Microsoft Visual Studio 2008-->Visual Studio Tools-->Visual Studio 2008 命令提示

键入:

makecert -r -pe -n "CN=MyServer" -ss My -sky exchange

解释一下:makecert.exe是一个专门用来制作证书的小工具,上面一行的意思就是制作一个CN=MyServer的服务器证书,默认存储在CurrentUser\My这个位置,同时这个证书标识为可导出。(详细的

MakeCert参数可参见http://msdn.microsoft.com/zh-cn/bfsktky3(vs.80).aspx)

再输入:

makecert -r -pe -n "CN=MyClient" -ss My -sky exchange

生成客户端证书,证书生成好以后,可以在IE里查看到,IE-->工具-->Internet选项-->内容-->证书

同时如何管理已经安装的证书,可参见http://www.cnblogs.com/yjmyzz/archive/2008/08/20/1272128.html

2.wcf服务端

vs.net2008启动后,新建一个控制台应用程序-->(右击)添加-->新建项-->WCF服务-->命名为MyService.cs-->保存

保存后,系统会自动生成一个接口文件IMyService.cs

二个文件的内容如下:
IMyService.cs

using System;  
using System.ServiceModel;

namespace Server
{
    // 注意: 如果更改此处的接口名称 "IMyService",也必须更新 App.config 中对 "IMyService" 的引用。
    [ServiceContract]
    public interface IMyService
    {
        [OperationContract]
        string Test();

    }
}

MyService.cs

using System; 
using System.ServiceModel;
namespace Server
{
    // 注意: 如果更改此处的类名 "MyService",也必须更新 App.config 中对 "MyService" 的引用。
    public class MyService : IMyService
    {
        public string Test()
        {
            Console.WriteLine("服务端输出:\n" + ServiceSecurityContext.Current.PrimaryIdentity.AuthenticationType);
            Console.WriteLine(ServiceSecurityContext.Current.PrimaryIdentity.Name);
            return "服务端时间:" + DateTime.Now.ToString(); 
        }
    }
}

再来新建一个cs文件:CustomX509CertificateValidator.cs
内容先贴在下面

using System;
using System.Security.Cryptography.X509Certificates;
using System.IdentityModel.Tokens;
using System.IdentityModel.Selectors;

namespace Server
{
    public class CustomX509CertificateValidator : X509CertificateValidator
    {
        public override void Validate(X509Certificate2 certificate)
        {
            Console.WriteLine(certificate.Subject);
            Console.WriteLine(certificate.Thumbprint); 
            if (certificate.Thumbprint != "3E4D4B64A90810B6CFF9B1DD2390D8C9488747BF")
                throw new SecurityTokenException("Certificate Validation Error!");
        }
    }
}

 

注意:项目必须先添加对System.IdentityModel的引用

解释一下:
这个文件的用户是:客户端要调用服务端方法,并提供客户端证书时,用来验证客户端证书的有效性。注意里面的if (certificate.Thumbprint != "3E4D4B64A90810B6CFF9B1DD2390D8C9488747BF")这一句,大家调试的时候,里面的3E4D4B64A90810B6CFF9B1DD2390D8C9488747BF要换成你自己的客户端证书的信息(每一个证书对应的这一串字符都是唯一的),可通过在IE浏览器里,查看MyClient证书的详细信息得到,见下图:

同时注意配置文件App.Config,内容如下

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <behaviors>
            <serviceBehaviors>
                <behavior name="Server.MyServiceBehavior">
                  <serviceMetadata httpGetEnabled="true" httpGetUrl="http://localhost:8080" />
                  <serviceDebug includeExceptionDetailInFaults="true" />
                  <serviceCredentials>
                    <clientCertificate>
                      <authentication certificateValidationMode="Custom" customCertificateValidatorType="Server.CustomX509CertificateValidator,Server"/>
                    </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="Server.MyServiceBehavior" name="Server.MyService">
                <endpoint address="net.tcp://localhost:8081" binding="netTcpBinding" contract="Server.IMyService" bindingConfiguration="NewBinding0"/>              
            </service>
        </services>
    </system.serviceModel>
</configuration>

 

解释一下:
<authentication certificateValidationMode="Custom" customCertificateValidatorType="Server.CustomX509CertificateValidator,Server"/></clientCertificate>
这一行的意思就是通知WCF服务端,验证客户端证书的模式为自定义,验证时调用Server.CustomX509CertificateValidator这个类来完成验证

<serviceCertificate findValue="MyServer" storeLocation="CurrentUser" x509FindType="FindBySubjectName" />
这一行的意思是WCF服务端验证证书时,到CurrentUser这个位置查询CN=MyServer的证书

最后在Program.cs里启用WCF,内容如下:

using System;  
using System.ServiceModel;

namespace Server
{
    class Program
    {
        static void Main(string[] args)
        {
            ServiceHost host = new ServiceHost(typeof(MyService));
            host.Open();
            Console.ReadKey();
        }
    }
}

 

build一下,如果编译无错的话,服务端完工,可以运行一下,将弹出一个DOS命令窗口(不过什么输出也没有),只要不报错,就表明Ok了,先不要急着关,尝试浏览一下:

http://localhost:8080/(这个地址哪里来的?回头看下App.config,里面有一行<serviceMetadata httpGetEnabled="true" httpGetUrl="http://localhost:8080/" />,呵呵,明白了吧) 正常的话,应该类似下图所示:

3.客户端调用

下面生成服务端的代理和配置文件,客户端开发将用到这二个文件,同样先进入vs2008的命令行状态,输入:

svcutil.exe http://localhost:8080/ /d:c:\123\

注意:输入这一行命令的时候,请确保服务端程序正在运行。这一句的意思就是在c:\123\目录下输出WCF的代理文件和配置文件

打开vs.net2008,再新建一个控制台应用程序,可以命名为Client

把c:\123\下生成的二个文件MyService.cs,output.config添加到Client项目中,同时将output.config改名为App.Config

Progam.cs代码内容如下:

using System; 
namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            using (MyServiceClient client = new MyServiceClient())
            {
                Console.WriteLine("客户端输出:");
                Console.WriteLine(client.Test());
            } 
            Console.ReadKey();
        }
    }
}

同时,参考下面的内容手动修改一下App.Config文件

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <system.serviceModel>
      <behaviors>
        <endpointBehaviors>
          <behavior name="NewBehavior">
            <clientCredentials>
              <clientCertificate findValue="MyClient" x509FindType="FindBySubjectName"/>
              <serviceCertificate>
                <authentication certificateValidationMode="None" />
              </serviceCertificate>
            </clientCredentials>
          </behavior>
        </endpointBehaviors>
      </behaviors>
      <bindings>
            <netTcpBinding>
                <binding name="NetTcpBinding_IMyService" 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:8081/" binding="netTcpBinding"
                bindingConfiguration="NetTcpBinding_IMyService" contract="IMyService"
                name="NetTcpBinding_IMyService" behaviorConfiguration="NewBehavior">
                <identity>
                    <dns value="MyServer" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>

 

主要是增加了一个节点
<behaviors>
        <endpointBehaviors>
          <behavior name="NewBehavior">
            <clientCredentials>
              <clientCertificate findValue="MyClient" x509FindType="FindBySubjectName"/>
              <serviceCertificate>
                <authentication certificateValidationMode="None" />
              </serviceCertificate>
            </clientCredentials>
          </behavior>
        </endpointBehaviors>
      </behaviors>

上面红色的行,就是表明客户端调用时,将用MyClient证书来验证

 

同时<endpoint address="net.tcp://localhost:8081/" binding="netTcpBinding"
                bindingConfiguration="NetTcpBinding_IMyService" contract="IMyService"
                name="NetTcpBinding_IMyService" behaviorConfiguration="NewBehavior">这一句,增加了behaviorConfiguration="NewBehavior"

好了,Build一下,没有问题的话,开发完成

4.测试:
先启动服务端,再启动客户端,运行结果如下:

(转贴请注明来自"菩提树下的杨过") http://www.cnblogs.com/yjmyzz/archive/2008/08/20/1272550.html

 

注意服务端server.exe输出的信息中3E4D4B64A90810B6CFF9B1DD2390D8C9488747BF与客户端证书完全吻合

最后来谈谈分发问题,上面这一系列测试都是在同一台机器完成的,客户端总不可能总是跟服务端在一台机器上,这个好办,在IE里把MyClient证书导出,注意导出时要选择"是,导出私钥",然后把导出的pfx文件连同客户端程序一起分发到目标客户机即可,这里要注意几点:

a.客户端上的App.config里,要把<endpoint address="net.tcp://localhost:8081/" 中的localhost换成服务端的Ip地址
b.注意防火墙参数设置(本例中,即要把tcp:8081端口打开)

时间: 2024-11-05 16:30:43

[原创]x.509证书在WCF中的应用(CS篇)的相关文章

[原创]x.509证书在WCF中的应用(Web/IIS篇)

在上一篇"x.509证书在WCF中的应用(CS篇)"里,我们知道了如何在应用程序中,利用x.509证书来验证WCF的消息安全(没看过的朋友建议先看下,地址http://www.cnblogs.com/yjmyzz/archive/2008/08/20/1272550.html),这一篇我们将尝试把x.509证书放到IIS里来验证WCF. WCF宿主在IIS和普通应用程序里,原理虽然没什么不同,但在实际测试中发现,如果服务端与客户端都采用x.509证书来验证,服务端设置的自定义验证客户端

[WCF安全系列]认证与凭证:X.509证书

在<上篇>中,我们谈到了常用的认证方式:用户名/密码认证和Windows认证.在下篇中,我们着重来介绍另外一种重要的凭证类型:X.509证书,以及针对X.509证书的认证方式.不过为了让读者能够真正地全面地了解X.509证书,我们需要先了解一些关于非对称密码学的背景知识. 目录 一.非对称密码学(Asymmetric Cryptography)     消息加密(Encryption)     数字签名(Digital Signature) 二.数字证书     数字证书的颁发机制     创

[WCF安全系列]谈谈WCF的客户端认证[X.509证书认证]

前面介绍Windows认证和用户名/密码认证这两种典型的客户端认证模式,我们最后来介绍最后一种客户端认证方式,即客户端凭证类型为X.509证书时服务端采用的认证,简称为证书认证.我们照例先看看看客户端证书凭证如何设置设置. 一.客户端证书凭证的设置 在服务认证一文中,我们知道了基于X.509证书证书的服务凭证通过X509CertificateRecipientServiceCredential类型表示.与之对应地,客户端凭证对应的类型是X509CertificateInitiatorClient

艾伟:[原创]谈谈WCF中的Data Contract(4):WCF Data Contract Versioning

软件工程是一门独特的工程艺术,需要解决的是不断改变的需求变化.而对于WCF,对于SOA,由于涉及的是对多个系统之间的交互问题,如何有效地解决不断改变的需求所带来的问题就显得更为重要:Service端版本的变化能否保持现有Consumer的正常调用,Consumer端的改变不至于影响对Service 的正常调用.对于Data Contract来说就是要解决这样的问题:Service端或者Client对Data Type的改变不会影响Service的正常调用. 在系统开发过程中,通过对Data Ty

[WCF安全系列]实例演示:TLS/SSL在WCF中的应用[SSL over TCP]

在接下来的系列文章中我们正是讨论关于身份认证的主题.在前面我们已经谈到了,WCF中的认证属于"双向认证",既包括服务对客户端的认证(以下简称客户端认证),也包括客户端对服务的认证(以下简称服务认证).客户端认证和服务认证从本质上并没有什么不同,无非都是被认证一方提供相应的用户凭证供对方对自己的身份进行验证.我们先来讨论服务认证,客户端认证放在后续的文章中. 在<从两种安全模式谈起>中,我们对TLS/SSL进行了简单的介绍.我们知道,客户端和服务在为建立安全上下文而进行的协商

我的WCF之旅(3):在WCF中实现双向通信(Bi-directional Communication)

昨天写了一篇Remoting中如何实现双向通信的文章<[原创].NET Remoting: 如何通过Remoting实现双向通信(Bidirectional Communication) >,作为对比,今天我们来讨论一下WCF的双向通信. 为了使我们能够更好地对比双向通信在Remoting中和WCF中的实现,我们的Sample采用一样的业务逻辑--调用一个数学计算的远程调用,除了传递相应的操作数之外,我们还传递一个对象,这个对象可以在Server端中回调 (Callback) 把运算结果在Cl

[原创-总结]WCF技术剖析系列总结篇

近半年以来,一直忙于我的第一本WCF专著<WCF技术剖析>的写作,一直无暇管理自己的Blog.到目前为止<WCF技术剖析(卷1)>的写作暂告一段落,初步预计于下个月由武汉博文视点出版.在<WCF技术剖析>写作期间,对WCF又有了新的感悟,为此以书名开始本人的第三个WCF系列.本系列的目的在于对<WCF技术剖析>的补充,会对书中的一些内容进行展开讲述,同时会囊括很多由于篇幅的原因忍痛割弃的内容. 1.通过一个ASP.NET程序模拟WCF基础架构 本系列的第一篇

WCF中的Binding模型之六(完结篇):从绑定元素认识系统预定义绑定

由于绑定对象由一系列有序的绑定元素组成,绑定元素最终决定着信道栈中信道的组成,而信道的组成最终又决定了信道栈对消息进行处理的方式和能力,所有要确定绑定的特性和能力,我们可以通过查看其绑定元素的构成来一窥究竟.为此我们我们写了一个简单的方法,用于列出一个具体的绑定对象所有的绑定元素,在介绍一个个具体的系统绑定中,我会使用该方法: 1: static void ListAllBindingElements(Binding binding) 2: { 3: BindingElementCollecti

我的WCF之旅(4):WCF中的序列化[上篇]

SOA 和Message Windows Communication Foundation (WCF) 是基于面向服务架构(Service Orientation Architecture--SOA)的一种理想的分布式技术(Distributed Technology), 相信在今后在建立基于SOA企业级别的解决方案和进行系统集成方面将会大有作为.一个基于SOA结构的互联系统(Connected System)通常由若干相互独立的子系统(Sub-System)组成,这些子系统可能一个独立的App