如何在 X509Chain 上打印有用的调试信息?

How to print useful debug info on an X509Chain?

X509Certificate class 有一个 ToString() 方法可以漂亮地打印所有有用的细节...... X509Chain class 没有。

我 运行 进入相当通用的 SslPolicyErrorsRemoteCertificateChainErrors。但是,我不知道从链中打印什么有助于调试。

documentation显示了很多元素遍历和打印。但是,我很困惑……我应该打印每个 ChainElement 的 ChainStatus,就像它们显示的那样,还是打印顶级 ChainStatus?

我想我会用扩展方法将其包装起来...我需要打印哪些信息才能进行调试 RemoteCertificateChainErrors

究竟是什么错误取决于解释(链中的证书之一可能无效或者您的主机可能不信任其中一个证书),但如果没有证书和状态,您将走不远来自每个元素。

这里有一些扩展方法,我用它来漂亮地打印来自链对象的有用调试信息。

    using System.Linq;
    using System.Security.Cryptography;
    using System.Security.Cryptography.X509Certificates;
    using System.Text;

    public static class X509Extensions
    {
        public static string ToDisplayString(this X509Chain chain)
        {
            var b = new StringBuilder();

            b.AppendLine($"[{nameof(chain.ChainPolicy.RevocationFlag)}]");
            b.AppendLine($"  {chain.ChainPolicy.RevocationFlag}");
            b.AppendLine();
            b.AppendLine($"[{nameof(chain.ChainPolicy.RevocationMode)}]");
            b.AppendLine($"  {chain.ChainPolicy.RevocationMode}");
            b.AppendLine();
            b.AppendLine($"[{nameof(chain.ChainPolicy.VerificationFlags)}]");
            b.AppendLine($"  {chain.ChainPolicy.VerificationFlags}");
            b.AppendLine();
            b.AppendLine($"[{nameof(chain.ChainPolicy.VerificationTime)}]");
            b.AppendLine($"  {chain.ChainPolicy.VerificationTime}");
            b.AppendLine();
            b.AppendLine($"[Application Policy]");
            foreach (var policy in chain.ChainPolicy.ApplicationPolicy)
            {
                b.AppendLine($"  {policy.ToDisplayString()}");
            }
            b.AppendLine();
            b.AppendLine($"[Certificate Policy]");
            foreach (var policy in chain.ChainPolicy.CertificatePolicy)
            {
                b.AppendLine($"  {policy.ToDisplayString()}");
            }
            b.AppendLine();
            b.AppendLine($"[Elements]");
            foreach (var (element, index) in chain.ChainElements.Cast<X509ChainElement>().Select((element, index) => (element, index)))
            {
                b.AppendLine();
                b.AppendLine($"[Element {index + 1}]");
                b.AppendLine();
                b.Append(element.Certificate.ToString());
                b.AppendLine();
                b.AppendLine($"[Status]");
                foreach (var status in element.ChainElementStatus)
                {
                    b.AppendLine($"  {status.ToDisplayString()}");
                }
            }

            return b.ToString();
        }

        public static string ToDisplayString(this Oid oid)
        {
            return oid.FriendlyName == oid.Value
                ? $"{oid.Value}"
                : $"{oid.FriendlyName}: {oid.Value}";
        }

        public static string ToDisplayString(this X509ChainStatus status)
        {
            return $"{status.Status}: {status.StatusInformation}";
        }
    }

您可以从任何远程证书验证回调中调用它。

bool CertificateValidationCallback(
    object sender,
    X509Certificate certificate,
    X509Chain chain,
    SslPolicyErrors errors)
{
    if (errors == SslPolicyErrors.None)
    {
        return true;
    }

    logger.LogError($"SSL Policy Error(s): {1}", errors);
    logger.LogError($"X509 Certificate:{0}{1}", Environment.NewLine, certificate.ToString());
    logger.LogError($"X509 Chain:{0}{1}", Environment.NewLine, chain.ToDisplayString());

    return false;
}