【问题标题】:C# X509 certificate validation, with Online CRL check, without importing root certificate to trusted root CA certificate storeC# X509 证书验证,带有在线 CRL 检查,无需将根证书导入受信任的根 CA 证书存储
【发布时间】:2021-08-04 11:32:46
【问题描述】:

我正在尝试验证 X509 证书链而不将根 CA 证书导入受信任的根 CA 证书存储(在生产中,此代码将在 Azure 函数中运行,you can't add certificates to the trusted root CA certificate store on Azure App Services)。

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

我对此进行了搜索,发现许多其他人都面临同样的问题,但这些建议似乎都不起作用。我遵循了this SO 帖子中概述的方法,这与 dotnet/runtime GitHub 上的问题#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 可能会节省一天的时间。充气城堡是实现这一目标的另一种选择吗?

【问题讨论】:

  • 错误消息的重要部分是“吊销服务器脱机”。检查证书的一种方法是使用证书进行连接。因此,如果您在应用程序中创建服务器并与本地主机建立虚拟连接并且 i 连接完成,那么您就知道证书是好的。错误提示服务器处于离线状态,因此您需要通过创建套接字来创建真正的服务器。
  • 嗨@jdweng,感谢您的回复。 CRL 肯定是在线的,因为如果我将根 CA 证书添加到我受信任的根存储中,所有三个错误都会消失。此外,我可以浏览到 CRL 并下载它。
  • 我认为你可能会失败,因为 TLS 版本是错误的。尝试将此静态方法添加到代码的开头,这有时会有所帮助:ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;如果 TLS 失败,则连接将失败。
  • @jdweng 抱歉,但您的所有假设和建议都与 OP 问题无关。

标签: c# .net .net-core x509


【解决方案1】:

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

是的。

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

离线撤销

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

RevocationStatusUnknown

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

UntrustedRoot

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


其他注意事项:确定证书有效的正确方法是从chain.Build 中获取布尔返回值。对于您当前的链,如果您禁用了撤销 (chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck),则 Build 将返回 true... 但 UntrustedRoot 错误仍将出现在 ChainStatus 输出中。如果有任何错误VerificationFlags 没有说要忽略,则Build 的布尔返回值为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}");
    }
}

【讨论】:

  • 非常感谢您的详细回复 - 这一切都说得通。我唯一悬而未决的问题是X509VerificationFlags.AllowUnknownCertificateAuthority 选项如果不允许不受信任的根 CA 有什么意义?
  • @simon-pearson 它允许并且不允许它们......这意味着 Build 不会因为根未知而返回 false。如果您还说撤销状态未知是可以的,那么构建就会通过(没有任何内容过期或链接不良)。但是,是的,这有点奇怪。
  • 我刚刚重新阅读了您的回复。所以你是说X509Chain 类只执行 CRL 检查链是否可以构建到受信任的根?并不是说我不相信你(我相信!),但你能把我链接到一些关于这个的阅读吗?对于 X509Chain 类的验证检查,我仍然对这里的基本原理感到有些不安。
  • 重新表述上述问题:所以您是说 X509Chain 类仅执行 CRL 检查链是否可以构建到受信任的根?这是在标准中定义的并且对所有 CRL 检查实现都是通用的,还是这是 Microsoft 在编写 X509Chain 类时做出的实现决定?你能把我链接到一些关于这个的阅读吗?您还可以提供任何链接来阅读导致 X509Chain.Build 返回 true 但仍返回 UntrustedRoot 错误的 AllowUnknownCertificateAuthority 标志的这种奇怪行为吗?
  • 压力不够我没有质疑你在说什么 - 我对 X509Chain 类正在执行的检查的理解有点不完整,我希望做一些背景阅读来填补在空白处。
【解决方案2】:

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

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

我们通过将证书上传到应用程序(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);
}

比我们实施了许多验证器:用于主题、时间、证书链(您基本上以某种方式标记您的根,然后从您在商店中验证链的证书开始,看看您是否最终进入您的根)和 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-web-app)。

【讨论】:

  • 嗨马克西姆,感谢您的回复。如果我的理解是正确的,您是说我们将不得不手动滚动我们的证书验证码 - 我真的希望它不会变成那样!我不明白X509VerificationFlags.AllowUnknownCertificateAuthority 标志的用途是什么?
  • 据我了解,此标志用于验证不以受信任根结尾的链,即 Thawte 等,而是以您自己的自签名证书结尾。
猜你喜欢
  • 2018-06-30
  • 1970-01-01
  • 2013-12-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-06
  • 2021-01-17
相关资源
最近更新 更多