【问题标题】:Abnormal behavior X509Chain.Build() on Windows and Linux in .NET Core 3.1.NET Core 3.1 中 Windows 和 Linux 上的异​​常行为 X509Chain.Build()
【发布时间】:2021-07-21 18:48:22
【问题描述】:

以下是我的证书层次结构:-

我有一个嵌入式设备,它使用带有客户端证书身份验证的 TLS 1.2 安全通道通过 websocket 协议连接到我的后端服务器。我的组织拥有自己的设备和后端服务器证书颁发机构。设备正在以.pfx 的格式向后端服务器生成其证书(没有完整的链)。以下是我的代码 sn-p

public static void ConfigureEndPoints(this KestrelServerOptions options)
{
    var configuration = options.ApplicationServices.GetRequiredService<IConfiguration>();
    foreach (var address in ipAddresses)
                {
                    switch ((EndPointType)config.Scheme)
                    {
                        case EndPointType.Plain:
                            options.Listen(address, port);
                            break;
                        case EndPointType.WithClientCert:
                            if (!string.IsNullOrEmpty(config.RootCertFile))
                                mRootCertFileName = config.RootCertFile;
                            if (config.IntermediateCerts != null)
                                mIntermediateCertFileNames = config.IntermediateCerts;
                            options.Listen(address, port, listenOptions =>
                            {
                                listenOptions.UseHttps(config.FileName, config.Password, configureOptions =>
                                {
                                    configureOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
                                    configureOptions.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls | SslProtocols.Tls11;

                                    
                                    configureOptions.ClientCertificateValidation += VerifyClientCertificate;
                                  
                                });
                            });
                            break;
                        case EndPointType.WithoutClientCert:
                            options.Listen(address, port, listenOptions =>
                            {
                                listenOptions.UseHttps(config.FileName, config.Password, configureOptions =>
                                {
                                    configureOptions.ClientCertificateMode = ClientCertificateMode.NoCertificate;
                                    configureOptions.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls | SslProtocols.Tls11;
                                });
                            });
                            break;
                        default:
                            Console.WriteLine($"Invalid scheme value {config.Scheme}");
                            break;
                    }
                }

     private static bool VerifyClientCertificate(X509Certificate2 cert, X509Chain chain, SslPolicyErrors sslPolicy)
        {
            List<X509Certificate2> intermediateCerts = null;
            try
            {
                X509Certificate2 clientCert = new X509Certificate2(cert);

                if (mIntermediateCertFileNames != null)
                {
                    foreach (string certFileName in mIntermediateCertFileNames)
                    {
                        if (intermediateCerts == null)
                            intermediateCerts = new List<X509Certificate2>();
                        X509Certificate2 clientIntermediateCert = new X509Certificate2(certFileName);
                        intermediateCerts.Add(clientIntermediateCert);
                    }
                }
                else
                {
                    return false;
                }
                List<X509Certificate2> additionalCerts = intermediateCerts.ToList();
                if (!string.IsNullOrEmpty(mRootCertFileName))
                    additionalCerts.Add(new X509Certificate2(mRootCertFileName));
                else
                {
                    return false;
                }
                bool status = VerifyCertificate(clientCert, additionalCerts);
                if (status)
                {
                    if (!(clientCert.NotAfter.ToUniversalTime() >= DateTime.Now.ToUniversalTime() &&
                          clientCert.NotBefore.ToUniversalTime() <= DateTime.Now.ToUniversalTime()))
                    {
                        status = false;
                    }
                }
                return status;
            }
            catch(Exception e)
            {
                return false;
            }
        }


     static bool VerifyCertificate(X509Certificate2 clientCert, List<X509Certificate2> additionalCerts)
        {
            
            bool status = true;
            bool thumbPrintMatch;
            var chain = new X509Chain();
            chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
            chain.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreWrongUsage;
            chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
            foreach (var cert in additionalCerts)
            {
                chain.ChainPolicy.ExtraStore.Add(cert);
            }

            try
            {
                status = chain.Build(clientCert);
            }
            catch (Exception e)
            {
                
                Console.WriteLine(e.Message);
            }
            

           
            
            if (status)
            {  
                if (chain.ChainElements.Count != chain.ChainPolicy.ExtraStore.Count + 1)
                {
                    return false;
                }

                for(var i = 1; i < chain.ChainElements.Count; i++)
                {
                    thumbPrintMatch = false;
                    for(var j = 0; j <= i; j++)
                    {
                        
                        if(chain.ChainElements[i].Certificate.Thumbprint == chain.ChainPolicy.ExtraStore[j].Thumbprint)
                        {
                            thumbPrintMatch = true;
                            break;
                        }
                    }
                    if (!thumbPrintMatch)
                    {
                        return false;
                    }
                }

            }
            else
                return false;

            return status;
        }

当我在 Windows 商店中添加所有必需的根证书和中间证书时,上面的代码在 Windows 10 上运行得非常好。 status = chain.Build(clientCert); 提供status as true 并且还提供chain.ChainElements.Count 为4,这是正确的,即它正在构建从叶证书到根CA 的设备证书的完整链(总共4 个)。所有这些在Windows 上都可以正常工作而无需添加@ 987654329@标志。

现在在 Linux (CentOS7) 上,我在 /etc/pki/ca-trust/source/anchors 文件夹中添加了 TestRootCA.crt、DeviceIntermediate1.crt 和 DeviceIntermediate2.crt 并运行了 update-ca-trust extract 命令。以下是之后的观察:-

  1. status = chain.Build(clientCert); 不会返回 true,直到我添加 chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority; 标志。我不想添加这个标志来实现严格的安全约束。

  2. 如果我添加了chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority; 标志,那么chain.ChainElements.Count 也只给出 1,即设备的叶证书。无法构建完整的链。

我还做了一件事,如果我让我的设备以.p12 格式生成具有完整链的证书,那么我的服务器就能够正确地对其进行身份验证。但是我不确定修改设备以提供完整链证书是否正确?是否存在一些安全威胁?

.NET CORE 在哪里搜索所有信任证书? 如何修改我的代码,以便我也可以对 linux 环境实施严格的安全约束,.pfx 无需发送设备链。

希望我能够解释所有事情。任何与此相关的想法都会有很大帮助。

【问题讨论】:

  • 它在certs 目录和cert.pem 文件中查找openssl version -d 报告的任何目录。开始调试的地方是第一个丢失的证书,例如中间设备 2.
  • 它在 /etc/pki/tls/cert.pem 文件中可用。 cert.pem 文件中提供了所有三个 CA TestRootCA、DeviceIntermediate1CA 和 DeviceIntermediate2CA。
  • 嗯。如果openssl verify -show_chain clientcert.pem 成功并且看起来正确,那么 X509Chain 应该可以工作。如果这不起作用,那么 OpenSSL 不喜欢您的 CA/链的某些特征,并且 X509Chain 将不起作用。 (如果它“应该有效,但无效”,请在 github.com/dotnet/runtime/issues 上打开一个问题,但除非我们可以在本地重现该错误,否则我们将无能为力。)
  • 我收到error 20 at 0 depth lookup: unable to get local issuer certificate 的错误。当我跑openssl verify -show_chain clientcert.pem。对此有任何想法。
  • 嗯,这就是 .NET 不喜欢链的原因,那么 :)。在 Windows 上工作而不是在 OpenSSL 上工作很少见,但确实会发生。如果不查看客户端证书(公共部分)和颁发者证书的示例,我认为没有什么好方法可以提供帮助。

标签: .net-core tls1.2 client-certificates


【解决方案1】:

我可以通过修改我的设备(网关)以在客户端身份验证期间将其证书及其完整链发送到服务器来解决此问题。 我从中了解到了几件事:-

  1. 设备必须将其证书及其完整链发送到服务器。
  2. 不需要在服务器上安装设备链的中间证书,这些必须由设备在其证书中发送。
  3. Windows 会自动构建完整链,即使设备未发送其完整链但设备中间证书已安装在 Windows 证书存储中。这就是在 Windows 上没有观察到此问题的原因。
  4. Linux 没有以这种方式运行。如果设备没有发送完整链的证书,即使中间设备证书安装在 Linux 证书存储中,它也不会构建完整链。

【讨论】:

    猜你喜欢
    • 2020-09-27
    • 2020-12-05
    • 2017-11-04
    • 1970-01-01
    • 1970-01-01
    • 2021-10-02
    • 2021-06-03
    • 1970-01-01
    • 2021-02-09
    相关资源
    最近更新 更多