如何在不导入根证书的情况下验证X509证书?

| 我的程序包含2个我知道并信任的根证书。 我必须验证信任中心的证书和信任中心颁发的\“ user \”证书,这些证书都源自这两个根证书。 我使用X509Chain类进行验证,但仅在根证书位于Windows证书存储区中时才有效。 我正在寻找一种无需导入theeses根证书的方式来验证证书的方法-以某种方式告诉X509Chain类,我确实信任此根证书,并且它应该仅检查链中的证书,而不能进行其他任何检查。 实际代码:
        X509Chain chain = new X509Chain();
        chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
        chain.ChainPolicy.ExtraStore.Add(root); // i do trust this
        chain.ChainPolicy.ExtraStore.Add(trust);
        chain.Build(cert);
编辑:这是一个.NET 2.0 Winforms应用程序。     
已邀请:
我发现解决方案不依赖于build方法的结果,而是检查ChainStatus属性。 (未在.NET 2.0上进行测试,但这是我针对此常见问题找到的唯一解决方案)
X509Chain chain = new X509Chain();
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.ExtraStore.Add(root);
chain.Build(cert);
if (chain.ChainStatus.Length == 1 &&
    chain.ChainStatus.First().Status == X509ChainStatusFlags.UntrustedRoot)
{
    // chain is valid, thus cert signed by root certificate 
    // and we expect that root is untrusted which the status flag tells us
}
else
{
    // not valid for one or more reasons
}
通过实验还发现
ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
即使您没有将证书添加到ExtraStore中,这也会使build方法返回true,这完全违背了检查的目的。我不建议出于任何原因使用此标志。     
我在dotnet / corefx上打开了一个问题,他们回答如下:   如果AllowUnknownCertificateAuthority是设置的唯一标志,则   if3ѭ将返回true         链正确终止于自签名证书中(通过   ExtraStore或搜索的持久存储)   根据所请求的吊销,没有证书无效   政策   所有证书在(可选)下均有效   ApplicationPolicy或CertificatePolicy值   所有证书的NotBefore值均在或之前   VerificationTime和所有证书的NotAfter值均为   (在验证时间之后)。         如果未指定该标志,则添加一个附加约束:      自签名证书必须在系统上注册为受信任(例如在LM \\ Root存储中)。      因此,Build()返回true,您知道时间有效的不可撤销链   存在。此时要做的事情已读   
chain.ChainElements[chain.ChainElements.Count - 1].Certificate
和   确定它是否是您信任的证书。我建议比较   
chainRoot.RawData
byte[]
代表您的证书   作为上下文中的根信任(即逐字节比较而不是   使用指纹值)。      (如果设置了其他标志,那么其他约束也将放宽) 所以你应该这样:
X509Chain chain = new X509Chain();
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.ExtraStore.Add(root);
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
var isValid = chain.Build(cert);

var chainRoot = chain.ChainElements[chain.ChainElements.Count - 1].Certificate;
isValid = isValid && chainRoot.RawData.SequenceEqual(root.RawData);
    
获得此方法的方法是编写自定义验证。 如果您在WCF上下文中,则可以通过对ѭ8进行子类化并在web.config中的serviceBehavior对象上指定自定义验证来完成:
<serviceBehaviors>
    <behavior name=\"IdentityService\">
      <serviceMetadata httpGetEnabled=\"true\" />
      <serviceDebug includeExceptionDetailInFaults=\"true\" />
      <serviceCredentials>
        <clientCertificate>
          <authentication customCertificateValidatorType=\"SSOUtilities.MatchInstalledCertificateCertificateValidator, SSOUtilities\"
            certificateValidationMode=\"Custom\" />
        </clientCertificate>
        <serviceCertificate findValue=\"CN=SSO ApplicationManagement\"
          storeLocation=\"LocalMachine\" storeName=\"My\" />
      </serviceCredentials>
    </behavior>
但是,如果您只是在寻找一种从其他主机接受SSL证书的方法,则可以在web.config文件中修改system.net设置:                     下面是一个X509CertificateValidator的示例,该示例测试LocalMachine / Personal存储中是否存在客户端证书。 (这不是您所需要的,但可以作为示例。
using System.Collections.Generic;
using System.Linq;
using System.Security;
using System.Security.Cryptography.X509Certificates;

/// <summary>
/// This class can be injected into the WCF validation 
/// mechanism to create more strict certificate validation
/// based on the certificates common name. 
/// </summary>
public class MatchInstalledCertificateCertificateValidator
    : System.IdentityModel.Selectors.X509CertificateValidator
{
    /// <summary>
    /// Initializes a new instance of the MatchInstalledCertificateCertificateValidator class.
    /// </summary>
    public MatchInstalledCertificateCertificateValidator()
    {
    }

    /// <summary>
    /// Validates the certificate. Throws SecurityException if the certificate
    /// does not validate correctly.
    /// </summary>
    /// <param name=\"certificateToValidate\">Certificate to validate</param>
    public override void Validate(X509Certificate2 certificateToValidate)
    {
        var log = SSOLog.GetLogger(this.GetType());
        log.Debug(\"Validating certificate: \"
            + certificateToValidate.SubjectName.Name
            + \" (\" + certificateToValidate.Thumbprint + \")\");

        if (!GetAcceptedCertificates().Where(cert => certificateToValidate.Thumbprint == cert.Thumbprint).Any())
        {
            log.Info(string.Format(\"Rejecting certificate: {0}, ({1})\", certificateToValidate.SubjectName.Name, certificateToValidate.Thumbprint));
            throw new SecurityException(\"The certificate \" + certificateToValidate
                + \" with thumprint \" + certificateToValidate.Thumbprint
                + \" was not found in the certificate store\");
        }

        log.Info(string.Format(\"Accepting certificate: {0}, ({1})\", certificateToValidate.SubjectName.Name, certificateToValidate.Thumbprint));
    }

    /// <summary>
    /// Returns all accepted certificates which is the certificates present in 
    /// the LocalMachine/Personal store.
    /// </summary>
    /// <returns>A set of certificates considered valid by the validator</returns>
    private IEnumerable<X509Certificate2> GetAcceptedCertificates()
    {
        X509Store k = new X509Store(StoreName.My, StoreLocation.LocalMachine);

        try
        {
            k.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
            foreach (var cert in k.Certificates)
            {
                yield return cert;
            }
        }
        finally
        {
            k.Close();
        }
    }
}
    
如果知道哪些证书可以作为要检查的证书的根证书和中间证书,则可以将根证书和中间证书的公钥加载到“ 12”对象的“ 11”集合中。 我的任务也是编写Windows Forms应用程序以安装证书,前提是该证书是根据我国政府已知的“国家根证书”颁发的。也有数量有限的CA \'s被允许颁发证书以验证与国家Web服务的连接,因此我拥有的证书数量有限,可以在链中使用,并且可能在目标计算机上丢失。我在应用程序的子目录“ cert \”中收集了CA \和政府根证书的所有公共密钥: 在Visual Studio中,我将目录cert添加到解决方案中,并将此目录中的所有文件标记为嵌入式资源。这使我能够在我的C#库代码中枚举“受信任的”证书的集合,从而建立一个链来检查证书,即使未安装颁发者证书也是如此。为此,我为X509Chain创建了一个包装器类:
private class X509TestChain : X509Chain, IDisposable
{
  public X509TestChain(X509Certificate2 oCert)
    : base(false)
  {
    try
    {
      ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
      ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
      if (!Build(oCert) || (ChainElements.Count <= 1))
      {
        Trace.WriteLine(\"X509Chain.Build failed with installed certificates.\");
        Assembly asmExe = System.Reflection.Assembly.GetEntryAssembly();
        if (asmExe != null)
        {
          string[] asResources = asmExe.GetManifestResourceNames();
          foreach (string sResource in asResources)
          {
            if (sResource.IndexOf(\".cert.\") >= 0)
            {
              try
              {
                using (Stream str = asmExe.GetManifestResourceStream(sResource))
                using (BinaryReader br = new BinaryReader(str))
                {
                  byte[] abResCert = new byte[str.Length];
                  br.Read(abResCert, 0, abResCert.Length);
                  X509Certificate2 oResCert = new X509Certificate2(abResCert);
                  Trace.WriteLine(\"Adding extra certificate: \" + oResCert.Subject);
                  ChainPolicy.ExtraStore.Add(oResCert);
                }
              }
              catch (Exception ex)
              {
                Trace.Write(ex);
              }
            }
          }
        }
        if (Build(oCert) && (ChainElements.Count > 1))
          Trace.WriteLine(\"X509Chain.Build succeeded with extra certificates.\");
        else
          Trace.WriteLine(\"X509Chain.Build still fails with extra certificates.\");
      }
    }
    catch (Exception ex)
    {
      Trace.Write(ex);
    }
  }

  public void Dispose()
  {
    try
    {
      Trace.WriteLine(string.Format(\"Dispose: remove {0} extra certificates.\", ChainPolicy.ExtraStore.Count));
      ChainPolicy.ExtraStore.Clear();
    }
    catch (Exception ex)
    {
      Trace.Write(ex);
    }
  }
}
在调用函数中,我现在可以成功检查是否有未知证书来自国家根证书:
    bool bChainOK = false;
    using (X509TestChain oChain = new X509TestChain(oCert))
    {
      if ((oChain.ChainElements.Count > 0)
        && IsPKIOverheidRootCert(oChain.ChainElements[oChain.ChainElements.Count - 1].Certificate))
        bChainOK = true;
      if (!bChainOK)
      {
        TraceChain(oChain);
        sMessage = \"Root certificate not present or not PKI Overheid (Staat der Nederlanden)\";
        return false;
      }
    }
    return true;
要完成此操作,请执行以下操作:检查根证书(通常已安装该证书,因为它已包含在Windows Update中,但理论上也可能会丢失),我将友好名称和指纹与已发布的值进行了比较:
private static bool IsPKIOverheidRootCert(X509Certificate2 oCert)
{
  if (oCert != null)
  {
    string sFriendlyName = oCert.FriendlyName;
    if ((sFriendlyName.IndexOf(\"Staat der Nederlanden\") >= 0)
      && (sFriendlyName.IndexOf(\" Root CA\") >= 0))
    {
      switch (oCert.Thumbprint)
      {
        case \"101DFA3FD50BCBBB9BB5600C1955A41AF4733A04\": // Staat der Nederlanden Root CA - G1
        case \"59AF82799186C7B47507CBCF035746EB04DDB716\": // Staat der Nederlanden Root CA - G2
        case \"76E27EC14FDB82C1C0A675B505BE3D29B4EDDBBB\": // Staat der Nederlanden EV Root CA
          return true;
      }
    }
  }
  return false;
}
我不确定此检查是否完全安全,但就我而言,Windows Forms应用程序的操作员一定可以访问要安装的有效证书。该软件的目的只是过滤证书列表,以帮助他仅在计算机的计算机存储中安装正确的证书(该软件还会安装中间证书和根证书的公共密钥,以确保证书的运行时行为。网络服务客户端正确)。     
我只是通过检查根证书是添加到ExtraStore的证书之一来扩展@Tristan的代码。
X509Chain chain = new X509Chain();
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.ExtraStore.Add(root);
chain.Build(cert);
if (chain.ChainStatus.Length == 1 &&
    chain.ChainStatus.First().Status == X509ChainStatusFlags.UntrustedRoot &&
    chain.ChainPolicy.ExtraStore.Contains(chain.ChainElements[chain.ChainElements.Count - 1].Certificate))
{
    // chain is valid, thus cert signed by root certificate 
    // and we expect that root is untrusted which the status flag tells us
    // but we check that it is a known certificate
}
else
{
    // not valid for one or more reasons
}
    

要回复问题请先登录注册