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

Rai*_*erM 12 .net verify x509certificate

我的程序包含我知道并信任的2个根证书.我必须验证信任中心的证书和信任中心颁发的"用户"证书,这些证书都来自这两个根证书.

我使用X509Chain类进行验证,但只有在根证书位于Windows证书库中时才有效.

我正在寻找一种方法来验证证书而不导入根证书 - 以某种方式告诉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);
Run Code Online (Sandbox Code Playgroud)

编辑:这是一个.NET 2.0 Winforms应用程序.

Tri*_*tan 6

我发现解决方案不依赖于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
}
Run Code Online (Sandbox Code Playgroud)

通过实验还发现

ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
Run Code Online (Sandbox Code Playgroud)

即使您没有将证书添加到ExtraStore中,这也完全使您无法实现检查的目的,这将使build方法返回true。我不建议出于任何原因使用此标志。


Dav*_*ian 6

我在dotnet / corefx上打开了一个问题,他们回答如下:

如果AllowUnknownCertificateAuthority是唯一设置的标志,则在以下情况下 chain.Build()将返回true:

  • 链正确终止于自签名证书中(通过ExtraStore或搜索的持久存储)

  • 根据所请求的吊销策略,没有证书无效

  • 所有证书在(可选)ApplicationPolicy或CertificatePolicy值下均有效

  • 所有证书的NotBefore值都在VerificationTime之前或之前,所有证书的NotAfter值都在VerificationTime之后(或之前)。

如果未指定该标志,则添加一个附加约束:

自签名证书必须在系统上注册为受信任的(例如,在LM \ Root存储中)。

因此,Build()返回true,您知道存在时间有效的不可撤销链。此时,将阅读 chain.ChainElements[chain.ChainElements.Count - 1].Certificate并确定它是否是您信任的证书。我比较推荐 chainRoot.RawDatabyte[]代表证书您在上下文中根信任(即字节为字节的比较,而不是使用指纹值)。

(如果设置了其他标志,那么其他约束也将放宽)

所以你应该这样:

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);
Run Code Online (Sandbox Code Playgroud)


fae*_*ter 1

获得此信息的方法是编写自定义验证。

如果您位于 WCF 上下文中,则可以通过System.IdentityModel.Selectors.X509CertificateValidator在 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>
Run Code Online (Sandbox Code Playgroud)

但如果您只是想找到一种方法来接受来自另一台主机的 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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)