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

时间:2016-08-30 00:00:42

标签: c# x509certificate2

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

以下是如何填充下拉列表(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()方法: `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();
    }

}`

1 个答案:

答案 0 :(得分:1)

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

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

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

因此简单的重复数据删除是用list = new List<X509Certificate2>()替换list = new HashSet<X509Certificate2>()(尽管您应该更改变量名称)。

HashSet只保留第一个碰撞;因此,如果您希望LocalMachine成为首选,那么您已经实现了这一点。如果CurrentUser赢了,你可能想要切换你的块。

另外两件事需要注意:

  1. 如果证书根本没有密钥用法扩展名,则认为它对所有用法都有效。你的代码没有这样做。 (如果您知道应用程序中的&#34;正确&#34;证书始终没有问题)

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