• 超详细的Asp.net使用SSL双向认证,一篇就够了


    传统:项目和项目之间https仅通过SSL单向认证后进行数据传输;
    本文:项目和项目之间做到SSL双向认证,防止攻击者恶意破坏;

    关于单向认证和双向认证的区别:https://cloud.tencent.com/developer/article/1819018

    本文一共分为4章节
    1章节:简述了服务器的网站在SSL双向认证中的角色分类;
    2章节:叙述了第三方在访问我方Server API时,我方需要做的配置和校验其携带的客户端证书;
    3章节:叙述了我方服务器上的网站作为客户端访问第三方Server API时需要做的配置和本地证书的配置与获取;
    4章节:简述了参考来源;


    1 叙述

    当前系统关于SSL双向认证包括两种不同的角色:
    1、当前系统作为服务器(第三方请求我方服务时,需要携带颁发给他们的客户端证书)
    2、当前系统作为客户端(我方请求第三方服务时,需要携带颁发给我方的客户端证书)
    3、(二者可以组合使用)
    后面章节分别对上述(1)(2)部分进行详细的描述;

    2 当前系统作为服务器

    2.1 IIS的配置

    在这里插入图片描述

    (1)当只有部分API需要Client Certificate时,勾选“接受”;(本文)
    在这里插入图片描述

    (2)当整个Service都必须Client Certificate时,勾选“要求SSL”–>“必须”;
    在这里插入图片描述

    2.2 相关代码

    2.2.1 代码验证片段

    下面代码所在的类继承了特性,重写了基类方法;

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    2.2.2 AppSetting.config设置

    Value用于配置允许哪些CN进行连接;多个CN之间通过分号隔开(自定义);
    在这里插入图片描述
    在这里插入图片描述

    2.3 计算机管理控制台MMC的配置

    Win+R --> mmc,打开控制台;如图所示,点击“文件”–>“添加/删除管理单元”;
    在这里插入图片描述

    左侧项目中选择“证书”,点击中间“添加”按钮,如下图所示,选择“我的用户账户”,其他的全部默认,点击完成;同样的方法选择“计算机账户”;
    (1)对于用户来说,用户个人计算机需要安装“客户端证书”时,只需要添加“我的用户账户”项;
    (2)对于服务器来说,只需要添加“计算机账户”;
    在这里插入图片描述 . 在这里插入图片描述
    在这里插入图片描述
    如下图所示,分别为配置“客户端证书”和“服务器证书”的主界面;
    在这里插入图片描述

    2.3.1 配置客户端证书

    2.3.1.1 添加客户端证书

    展开“证书 - 当前用户”节点,在“个人”–>“证书”右键,选择“所有任务”–>“导入”,点击“下一步”,选择“客户端.pfx”文件,点击“下一步”–> 输入文件的“密码”后,默认点击“下一步”,直到完成;此时客户端证书已经添加完毕;

    2.3.1.2添加客户端证书CA签发中心

    在“证书 - 当前用户”节点下,点击“受信任的根证书颁发机构”–>“证书”,右键“所有任务”–>“导入”,点击下一步,选择“.cer”文件,全部默认点击下一步,直到完成;此时客户端证书的CA签发中心已经添加完毕;
    注意:如果客户端证书和服务器证书的CA签发中心不一致,需要将服务器证书的CA签发文件(即.cer文件)上传到此处;

    2.3.2 配置服务器证书

    2.3.2.1 添加服务器证书

    选择“服务器.pfx”文件;与2.3.1.1中操作步骤一致;

    2.3.2.2 添加服务器证书CA签发中心

    与3.1.2中操作步骤一致;注意:如果客户端证书和服务器证书的CA签发中心不一致,需要将客户端证书的CA签发文件(即.cer文件)上传到此处;

    2.4 双向认证之IIS的log记录

    Log存储位置:C:\inetpub\logs\LogFiles,文件夹的最后一位编号对应IIS中应用程序的ID列,如图所示;
    在这里插入图片描述

    2.4.1 AWS Log记录示例

    提供正确的客户端证书(IIS Log):
    在这里插入图片描述

    提供自签名的客户端证书(IIS Log):
    在这里插入图片描述
    未提供客户端证书(网站Log4net记录)
    在这里插入图片描述
    ---->【Client certificates is null】

    提供了相关证书,但是未在appsetting.config中配置该CN时(网站Log4net记录)
    在这里插入图片描述

    2.5 注意事项

    2.5.1网站部署在局域网和远端的区别

    2.5.1.1网站部署局域网时

    部署在局域网内的网站,可以通过本地计算机来生成CA、服务器证书、客户端证书;
    通过局域网内两台电脑进行测试的测试结果:可通过需求中的SSL双向验证要求;

    2.5.1.2网站部署在远端时

    (1)部署在远端时,需要使用经过第三方CA进行签发的证书(需要一定的费用),也可自行通过网上尝试搜索“申请免费的计算机证书”进行获取;
    (2)自签名证书在远端不被信任,所以会被IIS拦截,可IIS的log中查看到相关拦截403记录(可参考4.1);
    (3)负载均衡不会将证书传递到另一台机器;

    2.5.2 Postman配置请求证书

    Postman配置客户端证书如下图所示(此文件由第三方提供)
    在这里插入图片描述

    2.5.3 与http层面的SSL双向握手的区别

    由于这里是通过程序进行客户端证书的验证,所以在请求API时,需要同时将客户端证书提交上来(Postman需要配置客户端证书和CA的PEM文件,PEM文件可通过网站进行下载);
    从http层面来说并不是实际意义上的双向认证,所以通过抓包只能看见单向验证,但是从某种意义上来说是更加严格的双向认证,其原因是因为通过“代码 + appsetting.config文件”限制了哪些CN可以通过;而http层面的不会去筛选特定的CN;

    3 当前系统作为客户端

    3.1 必备文件

    1、第三方提供的客户端证书.pfx文件;
    2、第三方提供的客户端证书的秘钥(明文,不要随意暴露);

    3.2 代码获取客户端证书的方式

    注意:如果证书经常过期,可以选择“通过文件路径获取证书”方式(Within 1 years.),这样可以免去通过MMC进行安装等复杂的配置步骤;

    3.2.1通过文件路径获取证书(非首选)

    注意:如果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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3.2.2获取通过MMC安装的证书(首选)

        #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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    3.3 MMC及IIS的配置

    3.3.1 MMC安装并配置客户端证书权限

    测试与实施人员:参见本文2.3章节安装客户端证书(仅在本地计算机 --> Local Machine);
    开发人员:参见本文2.3章节所有(Local Machine,Current User);

    3.3.2 配置客户端证书与IIS的权限

    简介:这里提供了两种方法来实现,基于网站的安全性考虑,建议通过MMC来配置客户端证书的访问权限;

    3.3.2.1 通过MMC配置客户端证书的访问权限(首选)

    Win+R --> mmc,打开控制台;如图所示,点击“文件”–>“添加/删除管理单元”–“证书”–“本地计算机”;如下图所示;
    在这里插入图片描述

    找到安装的指定的客户端证书后,“右键”–“所有任务”–“管理私钥”–>点击“添加”–>点击“高级”–>点击“立即查找”,如下图列表所示;
    在这里插入图片描述

    选择“IIS_IUSERS”–> 点击确定,–> 点击确定–>“应用”–>“确定”,完成
    在这里插入图片描述

    3.3.2.2 设置IIS程序池的角色权限(非首选)

    不建议使用此方式来设置(关于内置账户的区别,在第4章节有参考链接);
    步骤:打开IIS管理器,找到网站对应的程序池–>“高级设置”–“进程模型”–>“标识”–>“Local System”–>“确定”即可;
    在这里插入图片描述

    4 附录及参考

    4.1 第三章节参考信息

    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/

    欢迎指正和提问;

  • 相关阅读:
    git 回退的命令reset
    xshell 上传下载文件命令
    统计信号处理基础 习题解答10-15
    【内存管理】页面分配机制
    阿里巴巴为什么这样强制从List中删除元素
    SpringBoot日志输出
    【树莓派】raspberry pi控制超声波测距
    基于Java中的SSM框架实现网上花店管理平台项目【项目源码+论文说明】
    Python数学计算工具5、Python求最最小公倍数
    电子技术课设模电模电
  • 原文地址:https://blog.csdn.net/qq_39797713/article/details/126130544