【WCF安全】使用X509证书自定义验证
接触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证书自定义验证的更多相关文章
- WCF如何使用X509证书 z
WCF如何使用X509证书 如何创建证书: makecert.exe -sr LocalMachine -ss My -a sha1 -n CN=JiangServer -sky exchange - ...
- WCF如何使用X509证书
如何创建证书: makecert.exe -sr LocalMachine -ss My -a sha1 -n CN=JiangServer -sky exchange -pe (服务端证书) ...
- 【WCF安全】WCF 自定义授权[用户名+密码+x509证书]
1.x509证书制作(略) 2.直接贴代码 ----------------------------------------------------------------------服务端----- ...
- 重温WCF之WCF传输安全(十三)(4)基于SSL的WCF对客户端采用证书验证(转)
转载地址:http://www.cnblogs.com/lxblog/archive/2012/09/20/2695397.html 前一篇我们演示了基于SSL的WCF 对客户端进行用户名和密码方式的 ...
- XP机器上WCF采用X509证书加密时IIS读取证书的授权
XP机器上WCF采用X509证书加密时IIS读取证书的授权 XP下的授权命令为:winhttpcertcfg -g -c LOCAL_MACHINE\My -s 证书名称 -a "ASPNE ...
- WCF数据传输安全--数字证书
WCF 的传输安全涉及认证(客户端与服务器端双向认证).消息一致性(签名)和机密性(加密)三个主题. 常用认证方式: 第一:用户名/密码认证:wcf提供三种认证模式:1.将用户名映射到windows账 ...
- X509 证书生成
X509证书介绍X.509 是由国际电信联盟(ITU-T)制定的数字证书标准,相信这是人尽皆知的了,目前X.509证书据我所知有三个版本,.net中使用的是x.509-2,X.509-2 版引入了主体 ...
- OpenSSL 使用拾遗(二)---- X509 证书的 SKID/AKID 字段
SKID(证书使用者密钥标识符,subject key identifier 的简称)和 AKID(证书颁发机构密钥标识符,authority key identifier 的简称)是 X509 证书 ...
- SM2国密证书合法性验证
通常我们遇到过的X509证书都是基于RSA-SHA1算法的,目前国家在大力推行国密算法,未来银行发行的IC卡也都是基于PBOC3.0支持国密算法的,因此我们来学习一下如何验证SM2国密证书的合法性.至 ...
随机推荐
- Book Review of "The Practice of Programming" (Ⅰ)
The Practice of Programming In the preface, the author illustrates four basic principles of programm ...
- React Native中加载指示器组件ActivityIndicator使用方法
这里讲一下React Native中的一个组件——ActivityIndicator,这是一个加载指示器,俗称菊花,很常见的,效果如下所示: 可以看到图中有两个加载指示器,一大一小,这是尺寸不是我设置 ...
- iOS基于XMPP实现即时通讯之一、环境的搭建
移动端访问不佳,请访问我的个人博客 使用XMPP已经有一段时间了,但是一直都没深入研究过,只是使用SDK做一些简单的操作,看了许多大神的博客,自己总结一下,准备写一系列关于XMPP的使用博客,以便于自 ...
- 并发-ConcurrentHashMap源码分析
ConcurrentHashMap 参考: http://www.cnblogs.com/chengxiao/p/6842045.html https://my.oschina.net/hosee/b ...
- tp添加分页
//分页开始 $count=M('article')->where($condition)->count(); $p = intval($p) > 0 ? $p : 1; $page ...
- 电脑 HOST 文件
路径: C:\Windows\System32\drivers\etc\HOSTS
- Java对象序列化与反序列化
对象序列化的目标是将对象保存在磁盘中或者在网络中进行传输.实现的机制是允许将对象转为与平台无关的二进制流. java中对象的序列化机制是将允许对象转为字节序列.这些字节序列可以使Java对象脱离程序存 ...
- 资源(GitHub)
angular 文档 https://angular.cn/docs/ts/latest/quickstart.html http://sc.qq.com/fx/u?r=OfFE5AA ios h ...
- spring4x,暂时停更
spring4x,暂时停更 鄙人愚笨,没有spring基础,直接上了spring4x,发现无法理解(另外spring4x实战课本演示不详,本人学识有限),现从spring3开始.
- 如何让pycharm以py.test方式运行
第一步:进入File—Settings—Python Integrated Tools 发现设置中Default test runner是Unittests 将其改为py.test,点击OK保存 如果 ...