传统:项目和项目之间https仅通过SSL单向认证后进行数据传输;
本文:项目和项目之间做到SSL双向认证,防止攻击者恶意破坏;关于单向认证和双向认证的区别:https://cloud.tencent.com/developer/article/1819018
本文一共分为4章节
1章节:简述了服务器的网站在SSL双向认证中的角色分类;
2章节:叙述了第三方在访问我方Server API时,我方需要做的配置和校验其携带的客户端证书;
3章节:叙述了我方服务器上的网站作为客户端访问第三方Server API时需要做的配置和本地证书的配置与获取;
4章节:简述了参考来源;
当前系统关于SSL双向认证包括两种不同的角色:
1、当前系统作为服务器(第三方请求我方服务时,需要携带颁发给他们的客户端证书)
2、当前系统作为客户端(我方请求第三方服务时,需要携带颁发给我方的客户端证书)
3、(二者可以组合使用)
后面章节分别对上述(1)(2)部分进行详细的描述;
(1)当只有部分API需要Client Certificate时,勾选“接受”;(本文)
(2)当整个Service都必须Client Certificate时,勾选“要求SSL”–>“必须”;
下面代码所在的类继承了特性,重写了基类方法;
var reClient = context.Request.GetClientCertificate();
if (reClient == null)
{
_logService.Error($"Client Certificate is null.");
context.ErrorResult = new AuthenticationFailureResult($"Client Certificate is require.", context.Request, HttpStatusCode.BadRequest);
return;
}
var x509 = new X509Certificate2(reClient);
var chain = new X509Chain(true);
chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;//使用在线证书吊销列表(CRL)进行吊销检查
if (chain.Build(x509))
{
context.ErrorResult = new AuthenticationFailureResult($"Invalid Client Certificate.", context.Request, HttpStatusCode.BadRequest);
_logService.Error("Invalid Client Certificate. chain: " + chain.ToString());
return;
}
List<string> x509IssuerList = WC_X509_IssuerListString.Replace(" ","").Split(';').ToList();//去除空格,WC_X509_IssuerListString变量为Appsetting.config文件中配置的允许哪些CN的证书,分号作为分隔符,可配置多个CN
if (!x509IssuerList.Contains(x509.Issuer.Replace(" ", "")))
{
context.ErrorResult = new AuthenticationFailureResult($"", context.Request, HttpStatusCode.BadRequest);//Not return any message.
_logService.Error("Appsetting.config not exit x509Issuer: " + x509.Issuer.ToString());
return;
}
Value用于配置允许哪些CN进行连接;多个CN之间通过分号隔开(自定义);
Win+R --> mmc,打开控制台;如图所示,点击“文件”–>“添加/删除管理单元”;
左侧项目中选择“证书”,点击中间“添加”按钮,如下图所示,选择“我的用户账户”,其他的全部默认,点击完成;同样的方法选择“计算机账户”;
(1)对于用户来说,用户个人计算机需要安装“客户端证书”时,只需要添加“我的用户账户”项;
(2)对于服务器来说,只需要添加“计算机账户”;
.
如下图所示,分别为配置“客户端证书”和“服务器证书”的主界面;
展开“证书 - 当前用户”节点,在“个人”–>“证书”右键,选择“所有任务”–>“导入”,点击“下一步”,选择“客户端.pfx”文件,点击“下一步”–> 输入文件的“密码”后,默认点击“下一步”,直到完成;此时客户端证书已经添加完毕;
在“证书 - 当前用户”节点下,点击“受信任的根证书颁发机构”–>“证书”,右键“所有任务”–>“导入”,点击下一步,选择“.cer”文件,全部默认点击下一步,直到完成;此时客户端证书的CA签发中心已经添加完毕;
注意:如果客户端证书和服务器证书的CA签发中心不一致,需要将服务器证书的CA签发文件(即.cer文件)上传到此处;
选择“服务器.pfx”文件;与2.3.1.1中操作步骤一致;
与3.1.2中操作步骤一致;注意:如果客户端证书和服务器证书的CA签发中心不一致,需要将客户端证书的CA签发文件(即.cer文件)上传到此处;
Log存储位置:C:\inetpub\logs\LogFiles,文件夹的最后一位编号对应IIS中应用程序的ID列,如图所示;
提供正确的客户端证书(IIS Log):
提供自签名的客户端证书(IIS Log):
未提供客户端证书(网站Log4net记录)
---->【Client certificates is null】
提供了相关证书,但是未在appsetting.config中配置该CN时(网站Log4net记录)
部署在局域网内的网站,可以通过本地计算机来生成CA、服务器证书、客户端证书;
通过局域网内两台电脑进行测试的测试结果:可通过需求中的SSL双向验证要求;
(1)部署在远端时,需要使用经过第三方CA进行签发的证书(需要一定的费用),也可自行通过网上尝试搜索“申请免费的计算机证书”进行获取;
(2)自签名证书在远端不被信任,所以会被IIS拦截,可IIS的log中查看到相关拦截403记录(可参考4.1);
(3)负载均衡不会将证书传递到另一台机器;
Postman配置客户端证书如下图所示(此文件由第三方提供)
由于这里是通过程序进行客户端证书的验证,所以在请求API时,需要同时将客户端证书提交上来(Postman需要配置客户端证书和CA的PEM文件,PEM文件可通过网站进行下载);
从http层面来说并不是实际意义上的双向认证,所以通过抓包只能看见单向验证,但是从某种意义上来说是更加严格的双向认证,其原因是因为通过“代码 + appsetting.config文件”限制了哪些CN可以通过;而http层面的不会去筛选特定的CN;
1、第三方提供的客户端证书.pfx文件;
2、第三方提供的客户端证书的秘钥(明文,不要随意暴露);
注意:如果证书经常过期,可以选择“通过文件路径获取证书”方式(Within 1 years.),这样可以免去通过MMC进行安装等复杂的配置步骤;
注意:如果Log文件记录提示IIS无权限,则修改文件的访问权限,右键属性修改即可。
#region 方式1:通过路径获取Client Certificate方式;
try
{
string certificatePath = @"....\ClientCertificateFile.pfx";//使用相对路径或者绝对路径,
string certPassword = "******"; //客户端证书对应的明文密码;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3|SecurityProtocolType.Tls| (SecurityProtocolType)768|(SecurityProtocolType)3072|(SecurityProtocolType)0x300|(SecurityProtocolType)0xC00;//根据当前framework版本中的枚举进行设置;
ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;//获取或设置用于验证服务器证书的回调,根据实际情况true或false;
//clientCer对象就是请求时携带上去的客户端证书
X509Certificate2 clientCer = new X509Certificate2(certificatePath, certPassword, X509KeyStorageFlags.MachineKeySet|X509KeyStorageFlags.PersistKeySet|X509KeyStorageFlags.Exportable);
catch (Exception ex)
{
logger.Error($"Get Client certificate Error : {ex}.");
return null;
}
#endregion
#region 方式2:通过mmc安装Client Certificate方式
ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true; //获取或设置用于验证服务器证书的回调,根据实际情况true或false;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | (SecurityProtocolType)768 | (SecurityProtocolType)3072 | (SecurityProtocolType)0x300 | (SecurityProtocolType)0xC00;
#if !DEBUG
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine); //从 "本地计算机" 安装的Client Certificate中进行查找;//不能使用CurrentUser
#else
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);//从 "计算机当前用户" 安装的Client Certificate中进行查找;也可以使用LocalMachine
#endif
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
X509Certificate2 clientCer;
try
{
//var clintCertificatesList = store.Certificates.Cast().Select(c => c.Thumbprint).ToList();
//foreach (var VARIABLE in clintCertificatesList)
//{
//logger.Info($" {VARIABLE}.");//可以通过log4net打印MMC中所有证书的指纹,用于调试使用;
//}
//do not use "store.Certificates.Find()" --> 性能不好
//Find By certificate thumbprint.
//Case must be omitted;
clientCer = store.Certificates.Cast<X509Certificate2>().FirstOrDefault(c => c.Thumbprint.Equals(“***证书指纹***”, StringComparison.OrdinalIgnoreCase)); //linq语法,根据证书指纹获取指定证书
if (clientCer == null)
{
logger.Error($"Client certificate is null.");
return null;
}
}
catch (Exception ex)
{
logger.Error($"Get Client certificate Error : {ex}.");
return null;
}
#endregion
测试与实施人员:参见本文2.3章节安装客户端证书(仅在本地计算机 --> Local Machine);
开发人员:参见本文2.3章节所有(Local Machine,Current User);
简介:这里提供了两种方法来实现,基于网站的安全性考虑,建议通过MMC来配置客户端证书的访问权限;
Win+R --> mmc,打开控制台;如图所示,点击“文件”–>“添加/删除管理单元”–“证书”–“本地计算机”;如下图所示;
找到安装的指定的客户端证书后,“右键”–“所有任务”–“管理私钥”–>点击“添加”–>点击“高级”–>点击“立即查找”,如下图列表所示;
选择“IIS_IUSERS”–> 点击确定,–> 点击确定–>“应用”–>“确定”,完成
不建议使用此方式来设置(关于内置账户的区别,在第4章节有参考链接);
步骤:打开IIS管理器,找到网站对应的程序池–>“高级设置”–“进程模型”–>“标识”–>“Local System”–>“确定”即可;
1、IIS应用程序池标识的权限说明
https://blog.csdn.net/u014088408/article/details/98732583
https://www.cnblogs.com/jfzhu/p/4067297.html
https://docs.microsoft.com/zh-cn/troubleshoot/developer/webapps/iis/www-authentication-authorization/understanding-identities
2、IIS无法读取本地证书问题(见解答):Advanced Setting–>Process Model–>Identity IIS默认的是权限较低,可以改成Networkservice即可(个人觉得这里改成LocalSystem是可以的,但是由于权限过高会不安全)。
https://q.cnblogs.com/q/54675/
欢迎指正和提问;