C# 如何确保X509Certificate2类不返回重复的签名证书?

C# 如何确保X509Certificate2类不返回重复的签名证书?,c#,x509certificate2,C#,X509certificate2,我有一个C#windows窗体应用程序。用户键入message、subject、to,并从下拉列表中选择签名证书,以使用X509Certificate2class对电子邮件进行签名 下面是如何填充下拉列表(ComboBox SigningCertList)的代码段: try { X509Certificate2[] certs; certs = CryptoHelper.GetSigningCertificateList(); SigningCertList.Items.

我有一个C#windows窗体应用程序。用户键入message、subject、to,并从下拉列表中选择签名证书,以使用
X509Certificate2
class对电子邮件进行签名

下面是如何填充下拉列表(ComboBox SigningCertList)的代码段:

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()方法: `公共静态X509Certificate2[]GetSigningCertificateList() { var list=新列表()


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

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

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

因此,简单的重复数据消除方法是将
list=new list()
替换为
list=new HashSet()
(尽管您可能应该更改变量名)

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

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

  • 如果证书根本没有密钥使用扩展,那么它被认为对所有使用都有效。您的代码不会这样做。(如果您知道应用程序中的“正确”证书总是正确的,那么就没有问题)

  • X509Store.Certificates每次调用都会返回新对象;您可以通过对不返回的证书(或针对.NET 4.5.2及以下版本重置)调用Dispose来减少终结


  • 您没有显示帮助程序代码,因此无法确定它搜索的位置。请注意,在MMC中,它允许您同时查看您的帐户和计算机帐户。如果您不知道什么是计算机帐户,请了解它,您可能在那里有一个重复的证书。在FormLoad上调用try代码,因此使用GetSigningCert执行搜索记住,在我的帖子中,下拉列表会在下拉列表中显示签名证书,即使没有找到(搜索所有存储)在MMC管理单元中。安装证书后,MMC管理单元会在个人商店中显示该证书,现在应用程序会在下拉菜单中显示两次列出的签名证书。只有下拉菜单中的第二个证书会对电子邮件进行签名。同意@LexLi,显示帮助代码。没问题。你能确定“帮助代码”是什么意思吗.
    CryptoHelper.GetSigningCertificateList
    是您的“帮助代码”。它是您问题的相关代码实际存在的地方。您的前两段触及了问题的一部分。我没有想到它可能安装在两个不同的商店中。我对它进行编码是为了“当前和本地存储中的所有唯一证书”。非常有趣。
    知道了这一点,您建议使用Hashset来处理副本,同时考虑您提到的序列号可能性。
    感谢您的解释和解决方案,这是一个很好的信息。
            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();
        }
    
    }`