【问题标题】:How to ensure X509Certificate2 class does not return duplicate signing certificates?如何确保 X509Certificate2 类不返回重复的签名证书?
【发布时间】:2017-01-06 02:46:11
【问题描述】:

我有一个 C# windows 窗体应用程序。用户键入消息、主题、收件人,并从下拉列表中选择签名证书以使用X509Certificate2 类对电子邮件进行签名。

以下是用于填充下拉列表 (ComboBox SigningCertList) 的 sn-p 的方式:

try
{
    X509Certificate2[] certs;
    certs = CryptoHelper.GetSigningCertificateList();
    SigningCertList.Items.AddRange(certs);
    SigningCertList.ValueMember = "SerialNumber";
    SigningCertList.DisplayMember = "FriendlyName";
    SigningCertList.SelectedIndexChanged += new System.EventHandler(SigningCertList_SelectedIndexChanged);
    SigningCertList.SelectedItem = 0;
}

症状很奇怪。组合框将显示我的签名证书(从 p12 文件安装)。但是,如果我加载 Windows 证书 MMC 管理单元,则在搜索时找不到它。重新安装证书后,我在 Windows 证书 MMC 管理单元中看到它,现在在下拉列表中重复。只有列表中的第二个(或最后一个/最近的)签名证书实际上对其进行了签名。

那么如何确保X509Certificate2 类不会返回重复的签名证书?

下面是 GetSigningCertificateList() 方法: `public static X509Certificate2[] GetSigningCertificateList() { var list = new List();

        int matches = 0;
        X509Store localStore = new X509Store(StoreLocation.LocalMachine);
        localStore.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);

        try
        {
            foreach (X509Certificate2 cert in localStore.Certificates)
                {
                    foreach (X509Extension extension in cert.Extensions)
                    {
                        X509KeyUsageExtension usageExtension = extension as X509KeyUsageExtension;

                        if (usageExtension != null)
                        {
                            bool matchesUsageRequirements = ((X509KeyUsageFlags.DigitalSignature & usageExtension.KeyUsages) == X509KeyUsageFlags.DigitalSignature);

                            if (matchesUsageRequirements)
                            {
                                list.Add(cert);
                                matches += 1;
                            }
                        }
                    }
                }
        }
        finally
        {
            localStore.Close();
        }

        X509Store userStore = new X509Store(StoreLocation.CurrentUser);
        userStore.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);

        try
        {
            foreach (X509Certificate2 cert in userStore.Certificates)
            {
                foreach (X509Extension extension in cert.Extensions)
                {
                    X509KeyUsageExtension usageExtension = extension as X509KeyUsageExtension;

                    if (usageExtension != null)
                    {
                        bool matchesUsageRequirements = ((X509KeyUsageFlags.DigitalSignature & usageExtension.KeyUsages) == X509KeyUsageFlags.DigitalSignature);

                        if ((matchesUsageRequirements) && cert.FriendlyName.IndexOf("MYcompanyname.",0) >= 0)
                        {
                            list.Add(cert);
                            matches += 1;
                        }
                    }
                }
            }
        }
        finally
        {
            userStore.Close();
        }

        return list.ToArray();
    }

}`

【问题讨论】:

  • 您没有显示帮助代码,因此不确定它在哪里搜索。请注意,在 MMC 中,它允许您查看您的帐户和计算机帐户。如果您不知道计算机帐户是什么,请学习它,您可能会在那里拥有重复的证书。
  • 在 FormLoad 上调用 try 代码,因此使用 GetSigningCertificateList() 方法执行搜索。请记住,根据我的帖子,即使在 MMC Snapin 中未找到(搜索所有商店),下拉列表也会在下拉列表中显示签名证书。安装证书后,MMC Snapin 将其显示在个人商店中,现在该应用程序会在下拉菜单中显示两次列出的签名证书。只有下拉列表中的第二个会签署电子邮件。
  • 同意@LexLi,显示帮助代码。
  • 没问题。你能确定你所说的“帮助代码”是什么意思吗?
  • CryptoHelper.GetSigningCertificateList 是您的“帮助代码”。这是与您的问题相关的代码实际存在的地方。

标签: c# x509certificate2


【解决方案1】:

您提到您在 MMC 中看不到证书,但在您的应用中看到了;并且当您通过 MMC 安装证书时,它会显示两次。这表明您正在使用 MMC 查看用户我的商店(或计算机的我的商店),但有问题的证书通常存在于其他位置。

一旦证书在两个不同的存储中注册(相同的存储名称,不同的位置 => 不同的存储),Windows 就不再认为它是重复的(例如,两个实例可以具有不同的私钥权限)。因此,虽然您的应用程序有副本,但(本质上)Windows 或 .NET 没有。

您可以通过标准的去重策略来防止重复,例如使用 HashSet 而不是 List。如果发行者和序列号相同,则默认的 .Equals 检查(由默认比较器执行)将匹配。只要您的证书来自公共 CA,那应该是唯一的;但私有 PKI 可以回收序列号或不保证唯一性。如果您担心,可以使用自定义比较器,该比较器使用您喜欢的任何匹配逻辑。

因此,简单的 dedup 是将 list = new List<X509Certificate2>() 替换为 list = new HashSet<X509Certificate2>()(尽管您可能应该更改变量名)。

HashSet 只保留第一个冲突;因此,如果您希望 LocalMachine 成为首选,那么您已经实现了这一点。如果 CurrentUser 应该获胜,您可能需要切换块。

另外两件值得注意的事情:

  1. 如果证书根本没有密钥使用扩展,则认为它对所有使用都有效。你的代码没有这样做。 (如果您知道您的应用程序中的“正确”证书始终存在,那么就没有问题)

  2. X509Store.Certificates 每次调用都会返回新对象;您可以通过在您不返回的证书上调用 Dispose 来减少最终确定(或 .NET 4.5.2 及更低版本的重置)。

【讨论】:

  • 您的前两段是问题的一部分。我没有想到它可以安装在两个不同的商店中。我对其进行了编码,以便获得“当前和本地商店中的所有唯一证书”。非常有趣。
    知道这一点后,您建议使用 Hashset 来处理重复项,注意您提到的序列号可能性。
    感谢您的解释和解决方案,很好的信息。
猜你喜欢
  • 2019-10-31
  • 1970-01-01
  • 2015-08-26
  • 1970-01-01
  • 1970-01-01
  • 2018-06-18
  • 2017-10-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多