接触WCF时间比较短,在项目中要使用X509证书,纠结好几天终于有了结论,因此为了方便日后查阅和园友交流特意单独将部分代码提出,并做以记录。

1.准备工作

制作X509证书,此处用到三个证书名称

导入证书步骤:

第一步:运行mmc 打开控制台,添加证书

将证书导入,再设置证书的读取权限

2.方便阅读下文,先展示下代码整体结构

3.编写服务代码(WcfSite工程下)

服务一:

 public class HelloService : IHelloService
{
public string DoWork()
{
return "Hello";
}
} [ServiceContract]
public interface IHelloService
{
[OperationContract]
string DoWork();
}

服务二:

 public class HiService : IHiService
{
public string DoWork()
{
return "Hi";
}
} [ServiceContract]
public interface IHiService
{
[OperationContract]
string DoWork();
}

服务三:

public class NiHaoService : INiHaoService
{
public string DoWork()
{
return "你好";
}
} [ServiceContract]
public interface INiHaoService
{
[OperationContract]
string DoWork();
}

服务写完之后开始着手准备X509的自定义验证

此实例的验证逻辑如下:

第一步:验证客户端x509证书的有效性;

当客户端试图调用服务资源时,首先进入此处进行x509的验证

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IdentityModel.Selectors;
using System.Security.Cryptography.X509Certificates;
using System.Xml;
using System.ServiceModel; namespace WcfSite.Code
{
/// <summary>
/// 验证X509证书
/// </summary>
public class X509Validator : System.IdentityModel.Selectors.X509CertificateValidator
{
/// <summary>
/// 日志路径
/// </summary>
private const string logPath = @"\Safety";
/// <summary>
/// CA序列号集合Key=CA,Value=SN
/// </summary>
private static Dictionary<string, string> SNList = new Dictionary<string, string>();
private static Object objlock = new object(); #region X509CertificateValidator重写
/// <summary>
/// 验证X509证书
/// </summary>
/// <param name="certificate"></param>
public override void Validate(X509Certificate2 certificate)
{
if (certificate == null)
{
throw new FaultException("请安装X509证书");
}
if (SNList.Count == )
{
GetX509SerialNumberList();
}
lock (objlock)
{
if (!SNList.ContainsKey(certificate.Subject) || !SNList.ContainsValue(certificate.SerialNumber.ToUpper()))
{
throw new FaultException("X509证书无效");
}
}
} #endregion #region 私有方法
/// <summary>
/// 获取x509证书序列号
/// </summary>
private void GetX509SerialNumberList()
{
XmlDocument doc = new XmlDocument();
doc.Load(AppDomain.CurrentDomain.BaseDirectory + "X509SerialNumbers.xml");
XmlNodeList nodes = doc.SelectNodes("X509SN/SerialNumbers/Number");
foreach (XmlNode node in nodes)
{
string ca = String.Empty;//CA名称
string sn = String.Empty;//CA序列号
foreach (XmlAttribute xa in node.Attributes)//校验用户名密码
{
if (xa.Name == "CA")
ca = xa.Value;
else if (xa.Name == "SN")
sn = xa.Value;
}
if (!String.IsNullOrEmpty(ca) && !String.IsNullOrEmpty(sn))
SNList.Add(ca, sn.ToUpper());
}
}
#endregion
}
}

第二步:验证客户端是否有权限调用将要调用的服务资源

验证客户端使用的x509证书是否有权限调用服务资源

 /// <summary>
/// 提供对服务操作的授权访问检查
/// </summary>
public class CustomServiceAuthorizationManager : System.ServiceModel.ServiceAuthorizationManager
{
/// <summary>
/// 日志路径
/// </summary>
private const string logPath = @"\Safety"; #region ServiceAuthorizationManager重写 /// <summary>
/// 检查授权
/// </summary>
/// <param name="operationContext"></param>
/// <returns></returns>
protected override bool CheckAccessCore(OperationContext operationContext)
{
//请求调用的资源url
string action = operationContext.RequestContext.RequestMessage.Headers.Action;
Console.ForegroundColor = ConsoleColor.Red;
Console.ForegroundColor = ConsoleColor.White;
//ClaimSet 表示与某个实体关联的声明的集合。
//获取与授权策略关联的声明集
foreach (System.IdentityModel.Claims.ClaimSet cs in operationContext.ServiceSecurityContext.AuthorizationContext.ClaimSets)
{
if (cs.Issuer == System.IdentityModel.Claims.ClaimSet.System)
{
foreach (System.IdentityModel.Claims.Claim claim in cs.FindClaims("http://tempuri.org/", System.IdentityModel.Claims.Rights.PossessProperty))
{
//校验是否有调用权限
if (claim.Resource.ToString() == action)
{
return true;//通过
}
else
{
string url = action.Substring(, action.LastIndexOf('/'));
if (claim.Resource.ToString() == url + "/all")//可以调用该服务下所有的方法
return true;
} }
}
}
return false;//不通过
} #endregion
}

获取服务端定义的资源权限集合

 /// <summary>
/// 查询用户可调用的资源
/// 定义一组用于对用户进行授权的规则
/// </summary>
public class CustomAuthorizationPolicy : System.IdentityModel.Policy.IAuthorizationPolicy
{
/// <summary>
/// 证书名称(角色)-资源集合
/// </summary>
private static Dictionary<string, List<string>> dicRoleResources = new Dictionary<string, List<string>>();
private static Object objlock = new object();
/// <summary>
/// 日志路径
/// </summary>
private const string logPath = @"\Safety";
string id = string.Empty;
public CustomAuthorizationPolicy()
{
id = new Guid().ToString();
}
public System.IdentityModel.Claims.ClaimSet Issuer
{
get { return System.IdentityModel.Claims.ClaimSet.System; }
}
public string Id
{
get { return id; }
} #region IAuthorizationPolicy方法实现 /// <summary>
/// 查询用户可调用的资源
/// </summary>
/// <param name="evaluationContext"></param>
/// <param name="state"></param>
/// <returns></returns>
public bool Evaluate(System.IdentityModel.Policy.EvaluationContext evaluationContext, ref object state)
{
bool flag = false;
bool r_state = false;
if (state == null) { state = r_state; } else { r_state = Convert.ToBoolean(state); }
if (!r_state)
{
List<System.IdentityModel.Claims.Claim> claims = new List<System.IdentityModel.Claims.Claim>();
foreach (System.IdentityModel.Claims.ClaimSet cs in evaluationContext.ClaimSets)
{
foreach (System.IdentityModel.Claims.Claim claim in cs.FindClaims
(System.IdentityModel.Claims.ClaimTypes.Name, System.IdentityModel.Claims.Rights.PossessProperty))
{
IEnumerable<string> resourceList = HelpGetServiceResourceByName(claim.Resource.ToString());
foreach (string str in resourceList)
{
//授权的资源
claims.Add(new System.IdentityModel.Claims.Claim("http://tempuri.org/", str, System.IdentityModel.Claims.Rights.PossessProperty));
}
}
}
evaluationContext.AddClaimSet(this, new System.IdentityModel.Claims.DefaultClaimSet(Issuer, claims)); r_state = true; flag = true;
}
else
{
flag = true;
}
return flag;
} #endregion #region 私有方法 /// <summary>
/// 通过证书名称(角色)获取资源
/// </summary>
/// <param name="caRole">证书名称(角色)</param>
/// <returns></returns>
private IEnumerable<string> HelpGetServiceResourceByName(string caRole)
{
if (dicRoleResources.Count == )
{
lock (objlock)
{
GetRoleResourceList();
}
}
return dicRoleResources[caRole];
}
/// <summary>
/// 读取所有证书名称(角色)的可访问资源
/// </summary>
private void GetRoleResourceList()
{
XmlDocument doc = new XmlDocument();
doc.Load(AppDomain.CurrentDomain.BaseDirectory + "RoleResourceConfig.xml");
XmlNodeList nodes = doc.SelectNodes("ResourceConfig/Role");
foreach (XmlNode node in nodes)
{
foreach (XmlAttribute xa in node.Attributes)
{
if (xa.Name == "Name") //查询角色下的所有资源
{
List<string> lists = new List<string>(); //资源集合
foreach (XmlNode xn in node.ChildNodes)
{
if (xn.Name == "Resource")
{
lists.Add(xn.InnerXml);
}
}
if (!dicRoleResources.ContainsKey(xa.Value))
dicRoleResources.Add(xa.Value, lists);
}
}
}
}
#endregion }

到此,所有服务方法和x509自定义验证代码都已完成。之后将要在配置文件中配置客户端x509的权限信息

首先配置客户端x509证书的序列号,因为此实例是通过序列号验证证书是否有效的(X509SerialNumbers.xml)

 <?xml version="1.0" encoding="utf-8" ?>
<X509SN>
<SerialNumbers>
<Number CA="CN=Test01CA" SN="0e9a8c9d238597ae47ef56eb7e3a0b61"/>
</SerialNumbers>
</X509SN>

然后配置客户端x509证书能访问的服务资源权限(RoleResourceConfig.xml)

 <?xml version="1.0" encoding="utf-8" ?>
<ResourceConfig>
<!--TEST1-->
<Role Name="Test01CA">
<!--格式:地址+方法名;all表示有权限访问该地址下所有的服务方法-->
<Resource>http://tempuri.org/IHiService/all</Resource>
<!--格式:地址+方法名;all表示有权限访问该地址下所有的服务方法-->
<Resource>http://tempuri.org/IHelloService/all</Resource>
</Role>
<!--TEST2-->
<Role Name="Test02CA">
<!--格式:地址+方法名;all表示有权限访问该地址下所有的服务方法-->
<Resource>http://tempuri.org/INiHaoService/all</Resource>
</Role>
</ResourceConfig>

最后一项,配置服务传说中的ABC

直接上web.config

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<services>
<service name="WcfSite.HiService" behaviorConfiguration="httpBehavior">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsBinding" contract="WcfSite.IHiService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:9903/HiService" />
</baseAddresses>
</host>
</service>
<service name="WcfSite.HelloService" behaviorConfiguration="httpBehavior">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsBinding" contract="WcfSite.IHelloService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:9903/HelloService" />
</baseAddresses>
</host>
</service>
<service name="WcfSite.NiHaoService" behaviorConfiguration="httpBehavior">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsBinding" contract="WcfSite.INiHaoService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:9903/NiHaoService" />
</baseAddresses>
</host>
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="wsBinding" closeTimeout="00:10:00" openTimeout="00:10:00"
receiveTimeout="01:00:00" sendTimeout="01:00:00" allowCookies="false"
bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="" maxReceivedMessageSize=""
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true">
<readerQuotas maxDepth="" maxStringContentLength="" maxArrayLength=""
maxBytesPerRead="" maxNameTableCharCount="" />
<reliableSession inactivityTimeout="01:00:00"/>
<security mode="Message">
<!--定义消息级安全性要求的类型,为证书-->
<message clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="httpBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceCredentials>
<serviceCertificate findValue="TestServiceCA" x509FindType="FindBySubjectName"
storeLocation="LocalMachine" storeName="My" />
<clientCertificate>
<!--自定义对客户端进行证书认证方式 这里为 None-->
<authentication certificateValidationMode="Custom"
customCertificateValidatorType="WcfSite.Code.X509Validator,WcfSite"/>
</clientCertificate>
</serviceCredentials>
<serviceAuthorization serviceAuthorizationManagerType="WcfSite.Code.CustomServiceAuthorizationManager,WcfSite">
<authorizationPolicies>
<add policyType="WcfSite.Code.CustomAuthorizationPolicy,WcfSite"/>
</authorizationPolicies>
</serviceAuthorization>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
</configuration>

到此编写服务端代码完成。

继续客户端的,比较简单:

直接引用服务,修改一下配置

先上客户端config代码

 <?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="wsBinding_Test" closeTimeout="00:10:00" openTimeout="00:10:00"
receiveTimeout="01:00:00" sendTimeout="01:00:00" allowCookies="false" bypassProxyOnLocal="false"
hostNameComparisonMode="StrongWildcard" maxBufferPoolSize=""
maxReceivedMessageSize="" messageEncoding="Text" textEncoding="utf-8"
useDefaultWebProxy="true">
<readerQuotas maxDepth="" maxStringContentLength="" maxArrayLength=""
maxBytesPerRead="" maxNameTableCharCount="" />
<reliableSession inactivityTimeout="01:00:00"/>
<security>
<message clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="myClientBehavior">
<clientCredentials>
<!--客户端证书-->
<clientCertificate findValue="Test01CA" storeName="My" storeLocation="LocalMachine"
x509FindType="FindBySubjectName"/>
<serviceCertificate>
<authentication certificateValidationMode="None"/>
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint address="http://localhost:9903/HiService.svc" binding="wsHttpBinding" behaviorConfiguration="myClientBehavior"
bindingConfiguration="wsBinding_Test" contract="_HiService.IHiService"
name="WSHttpBinding_IHiService">
<identity>
<dns value="TestServiceCA" />
</identity>
</endpoint>
<endpoint address="http://localhost:9903/NiHaoService.svc" binding="wsHttpBinding" behaviorConfiguration="myClientBehavior"
bindingConfiguration="wsBinding_Test" contract="_NiHaoService.INiHaoService"
name="WSHttpBinding_INiHaoService">
<identity>
<dns value="TestServiceCA" />
</identity>
</endpoint>
<endpoint address="http://localhost:9903/HelloService.svc" binding="wsHttpBinding" behaviorConfiguration="myClientBehavior"
bindingConfiguration="wsBinding_Test" contract="_HelloService.IHelloService"
name="WSHttpBinding_IHelloService">
<identity>
<dns value="TestServiceCA" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
  class Program
{
static void Main(string[] args)
{
try
{
_HelloService.HelloServiceClient hs = new _HelloService.HelloServiceClient();
Console.WriteLine("HelloService:" + hs.DoWork());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
try
{
_HiService.HiServiceClient hs = new _HiService.HiServiceClient();
Console.WriteLine("HiService:" + hs.DoWork());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
try
{
_NiHaoService.NiHaoServiceClient hs = new _NiHaoService.NiHaoServiceClient();
Console.WriteLine("NiHaoService:" + hs.DoWork());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
}

到这里 客户端和服务端的代码都算撸完了。

现在进入运行阶段

1.先将服务端启动,再启动客户端

2.出结果

。。。。。

源码下载 Wcfx509.rar

【WCF安全】使用X509证书自定义验证的更多相关文章

  1. WCF如何使用X509证书 z

    WCF如何使用X509证书 如何创建证书: makecert.exe -sr LocalMachine -ss My -a sha1 -n CN=JiangServer -sky exchange - ...

  2. WCF如何使用X509证书

    如何创建证书: makecert.exe -sr LocalMachine -ss My -a sha1 -n CN=JiangServer -sky exchange -pe     (服务端证书) ...

  3. 【WCF安全】WCF 自定义授权[用户名+密码+x509证书]

    1.x509证书制作(略) 2.直接贴代码 ----------------------------------------------------------------------服务端----- ...

  4. 重温WCF之WCF传输安全(十三)(4)基于SSL的WCF对客户端采用证书验证(转)

    转载地址:http://www.cnblogs.com/lxblog/archive/2012/09/20/2695397.html 前一篇我们演示了基于SSL的WCF 对客户端进行用户名和密码方式的 ...

  5. XP机器上WCF采用X509证书加密时IIS读取证书的授权

    XP机器上WCF采用X509证书加密时IIS读取证书的授权 XP下的授权命令为:winhttpcertcfg -g -c LOCAL_MACHINE\My -s 证书名称 -a "ASPNE ...

  6. WCF数据传输安全--数字证书

    WCF 的传输安全涉及认证(客户端与服务器端双向认证).消息一致性(签名)和机密性(加密)三个主题. 常用认证方式: 第一:用户名/密码认证:wcf提供三种认证模式:1.将用户名映射到windows账 ...

  7. X509 证书生成

    X509证书介绍X.509 是由国际电信联盟(ITU-T)制定的数字证书标准,相信这是人尽皆知的了,目前X.509证书据我所知有三个版本,.net中使用的是x.509-2,X.509-2 版引入了主体 ...

  8. OpenSSL 使用拾遗(二)---- X509 证书的 SKID/AKID 字段

    SKID(证书使用者密钥标识符,subject key identifier 的简称)和 AKID(证书颁发机构密钥标识符,authority key identifier 的简称)是 X509 证书 ...

  9. SM2国密证书合法性验证

    通常我们遇到过的X509证书都是基于RSA-SHA1算法的,目前国家在大力推行国密算法,未来银行发行的IC卡也都是基于PBOC3.0支持国密算法的,因此我们来学习一下如何验证SM2国密证书的合法性.至 ...

随机推荐

  1. Book Review of "The Practice of Programming" (Ⅰ)

    The Practice of Programming In the preface, the author illustrates four basic principles of programm ...

  2. React Native中加载指示器组件ActivityIndicator使用方法

    这里讲一下React Native中的一个组件——ActivityIndicator,这是一个加载指示器,俗称菊花,很常见的,效果如下所示: 可以看到图中有两个加载指示器,一大一小,这是尺寸不是我设置 ...

  3. iOS基于XMPP实现即时通讯之一、环境的搭建

    移动端访问不佳,请访问我的个人博客 使用XMPP已经有一段时间了,但是一直都没深入研究过,只是使用SDK做一些简单的操作,看了许多大神的博客,自己总结一下,准备写一系列关于XMPP的使用博客,以便于自 ...

  4. 并发-ConcurrentHashMap源码分析

    ConcurrentHashMap 参考: http://www.cnblogs.com/chengxiao/p/6842045.html https://my.oschina.net/hosee/b ...

  5. tp添加分页

    //分页开始 $count=M('article')->where($condition)->count(); $p = intval($p) > 0 ? $p : 1; $page ...

  6. 电脑 HOST 文件

    路径: C:\Windows\System32\drivers\etc\HOSTS

  7. Java对象序列化与反序列化

    对象序列化的目标是将对象保存在磁盘中或者在网络中进行传输.实现的机制是允许将对象转为与平台无关的二进制流. java中对象的序列化机制是将允许对象转为字节序列.这些字节序列可以使Java对象脱离程序存 ...

  8. 资源(GitHub)

    angular 文档 https://angular.cn/docs/ts/latest/quickstart.html   http://sc.qq.com/fx/u?r=OfFE5AA ios h ...

  9. spring4x,暂时停更

    spring4x,暂时停更 鄙人愚笨,没有spring基础,直接上了spring4x,发现无法理解(另外spring4x实战课本演示不详,本人学识有限),现从spring3开始.

  10. 如何让pycharm以py.test方式运行

    第一步:进入File—Settings—Python Integrated Tools 发现设置中Default test runner是Unittests 将其改为py.test,点击OK保存 如果 ...