繁体   English   中英

C# X509 证书验证,带在线 CRL 检查,无需将根证书导入受信任的根 CA 证书存储

[英]C# X509 certificate validation, with Online CRL check, without importing root certificate to trusted root CA certificate store

我正在尝试验证 X509 证书链而不将根 CA 证书导入受信任的根 CA 证书存储(在生产中,此代码将在 Azure Function 中运行,并且您无法将证书添加到受信任的根 CA 证书存储Azure 应用服务)。

我们还需要对此证书链执行在线 CRL 检查。

我对此进行了搜索,发现许多其他人都面临同样的问题,但似乎没有任何建议有效。 我遵循了这篇SO 帖子中概述的方法,这与 dotnet/runtime GitHub 上的 issue #26449中的建议相呼应。 这是一个重现问题的小型控制台应用程序(针对 .NET Core 3.1):

static void Main(string[] args)
{
    var rootCaCertificate = new X509Certificate2("root-ca-cert.cer");
    var intermediateCaCertificate = new X509Certificate2("intermediate-ca-cert.cer");
    var endUserCertificate = new X509Certificate2("end-user-cert.cer");

    var chain = new X509Chain();
    chain.ChainPolicy.ExtraStore.Add(rootCaCertificate);
    chain.ChainPolicy.ExtraStore.Add(intermediateCaCertificate);
    chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
    chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
    chain.Build(endUserCertificate);
    chain.Build(new X509Certificate2(endUserCertificate));

    var errors = chain.ChainStatus.ToList();
    if (!errors.Any())
    {
        Console.WriteLine("Certificate is valid");
        return;
    }

    foreach (var error in errors)
    {
        Console.WriteLine($"{error.Status.ToString()}: {error.StatusInformation}");
    }
}

运行时返回三个错误:

UntrustedRoot: A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider.
RevocationStatusUnknown: The revocation function was unable to check revocation for the certificate.
OfflineRevocation: The revocation function was unable to check revocation because the revocation server was offline.

但是,如果我将根 CA 证书添加到受信任的根 CA 证书存储中,则所有三个错误都会消失。

问题

  1. 这是我的实现有问题,还是我试图做的事情不可能?
  2. 我有什么选择来尝试实现这一目标? 一些谷歌搜索表明 .NET 5 中提供的X509ChainPolicy.CustomTrustStore可能会节省一天的时间。 充气城堡是实现这一目标的另一种选择吗?

好吧,不是一个完整的答案,但它可能会让你继续前进。

我们在 Azure Web 应用程序(不是 function 应用程序,但我想没关系)之前构建了您想要的应用程序。 花了我们一周左右的时间。 完整的答案是发布整个应用程序的代码,我们显然做不到。 不过有些提示。

我们通过将证书上传到应用程序(TLS 设置)并通过 WEBSITE_LOAD_CERTIFICATES 设置在代码中访问它们来解决证书问题(您将指纹放在那里,您发布的链接中也提到了),而不是您可以在代码中获取它们并构建您的自己的证书存储:

var certThumbprintsString = Environment.GetEnvironmentVariable("WEBSITE_LOAD_CERTIFICATES");
var certThumbprints = certThumbprintsString.Split(",").ToList();

var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
for (var i = 0; i < certThumbprints.Count; i++)
{
  store.Certificates.Find(X509FindType.FindByThumbprint, certThumbprint, false);
}

比我们实施了许多验证器:对于主题、时间、证书链(你基本上以某种方式标记你的根和 go 从证书中验证你的商店中的链,看看你是否最终进入你的根)和 CRL。

对于 CRL Bouncy Castle 提供支持:

// Get CRL from certificate, fetch it, cache it
var crlParser = new X509CrlParser();
var crl = crlParser.ReadCrl(data);
var isRevoked = crl.IsRevoked(cert);

从证书中获取 CRL 很棘手,但可行(我遵循此目的,或多或少https://docs.microsoft.com/en-us/archive/blogs/joetalksmicrosoft/pki-authentication-as-a-azure -网络应用程序)。

一些谷歌搜索表明 .NET 5 中提供的 X509ChainPolicy.CustomTrustStore 可能会节省一天的时间

是的。

不是将rootCaCertificate放入ExtraStore,而是放入CustomTrustStore,然后设置chain.ChainPolicy.TrustMode chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; . 现在您提供的根是唯一对链有效的根。 您还可以删除 AllowUnknownCertificateAuthority 标志。

离线撤销

这个错误有点误导。 这意味着“请求撤销链,但缺少一个或多个撤销响应”。 在这种情况下,它丢失了,因为构建器没有要求它,因为它不信任根。 (一旦您不信任根,您就无法信任 CRL/OCSP 响应,那么为什么要要求它们呢?)

撤销状态未知

同样,未知是因为它没有要求它。 此代码与 OfflineRevocation 不同,因为从技术上讲,有效的 OCSP 响应是(实际上)“我不知道”。 那将是在线/未知的。

不受信任的根

由上面的自定义信任代码解决。


其他注意事项:确定证书有效的正确方法是从 chain.Build 捕获 boolean 返回值。 对于您当前的链,如果您禁用了撤销 ( chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck ),那么 Build 将返回true ...但UntrustedRoot错误仍会出现在 ChainStatus output 中。 如果存在任何VerificationFlags 没有说要忽略的错误,则Build的 boolean 返回为false

您也只需要调用一次 Build :)。

static void Main(string[] args)
{
    var rootCaCertificate = new X509Certificate2("root-ca-cert.cer");
    var intermediateCaCertificate = new X509Certificate2("intermediate-ca-cert.cer");
    var endUserCertificate = new X509Certificate2("end-user-cert.cer");

    var chain = new X509Chain();
    chain.ChainPolicy.CustomTrustStore.Add(rootCaCertificate);
    chain.ChainPolicy.ExtraStore.Add(intermediateCaCertificate);
    chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
    chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
    bool success = chain.Build(endUserCertificate);

    if (success)
    {
        return;
    }

    foreach (X509ChainStatus error in chain.ChainStatus)
    {
        Console.WriteLine($"{error.Status.ToString()}: {error.StatusInformation}");
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM