X509Store证书存在问题。FindByThumbprint查找方法

99

我在使用X509Store.Certificates.Find方法时遇到了问题。

public static X509Certificate2 FromStore(StoreName storeName, 
          StoreLocation storeLocation, X509FindType findType, string findValue)
{
    X509Store store = new X509Store(storeName, storeLocation);
    store.Open(OpenFlags.ReadOnly);
    try
    {
        //findValue = "7a6fa503ab57b81d6318a51ca265e739a51ce660"
        var results = store.Certificates.Find(findType, findValue, true);

        return results[0];                
    }
    finally
    {
        store.Close();
    }
}
在这种情况下,Find方法返回0个结果(results.Count == 0),但是如果我将findValue设置为常量,该方法会找到证书。
public static X509Certificate2 FromStore(StoreName storeName, 
           StoreLocation storeLocation, X509FindType findType, string findValue)
{
    X509Store store = new X509Store(storeName, storeLocation);
    store.Open(OpenFlags.ReadOnly);
    try
    {         
        //findValue= "7a6fa503ab57b81d6318a51ca265e739a51ce660"
        var results = store.Certificates.Find(findType, 
                              "7a6fa503ab57b81d6318a51ca265e739a51ce660", true);
        return results[0];
    }
    finally
    {
        store.Close();
    }
}
14个回答

147
我猜想您已经将指纹从Windows证书信息对话框复制粘贴到了您的代码中(或者如果这是一个简化的示例,则复制到配置文件中)。令人恼火的是,指纹文本框中的第一个字符是不可见的Unicode“从左到右标记”控制字符。尝试选择开头的字符串引号和指纹的第一个字符,删除它们(这也会去除它们之间的不可见字符),然后手动重新输入它们。
我今天自己遇到了这种奇怪的行为,花了我一个多小时才弄清楚。最终我是通过使用调试器来检查findValue和证书对象的Thumbprint的长度和哈希码来看到它的。结果发现它们是不同的。这导致我在调试器中检查这些字符串的字符数组时,发现了这个不可见的字符。

4
不必重新输入的更简单方法是从证书管理控制台对话框中复制指纹,然后将其粘贴到文本编辑器(如Notepad ++)中,此时不可见的Unicode字符将显示为“?”或其他明显奇怪的字符。然后您可以删除该字符并将“更新后”的字符串复制到您的代码/配置/文本框中。 - nateirvin
2
@nateirvin:True(我的建议是手动重新输入有点过头了,这是因为我当时非常沮丧)- 或者将其粘贴到UTF-8模式并打开隐藏字符的显示(这更有趣,因为它可以向您显示确切的字符)。 - Aasmund Eldhuset
1
错误记录在这里 https://support.microsoft.com/zh-cn/kb/2023835 教训是不要从MMC复制粘贴。 - Darryl Braaten
3
记录一下,指纹识别是不区分大小写的。在VS2015和记事本中,我可以通过按删除键来删除看不见的字符,并通过光标键确认其存在。 - Simon_Weaver
1
在 UTF-8 中,有问题的字符(U+200E)将被编码为这三个字节:0xE2808E。 - SSS
显示剩余3条评论

57

我将这里的一些答案结合起来,创建了一个静态方法,它可以处理特殊字符并将所有字母转换为大写。希望其他人也能用得上。

public static X509Certificate2 GetCertificate(string thumbprint)
{
    // strip any non-hexadecimal values and make uppercase
    thumbprint = Regex.Replace(thumbprint, @"[^\da-fA-F]", string.Empty).ToUpper();
    var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);

    try
    {
        store.Open(OpenFlags.ReadOnly);

        var certCollection = store.Certificates;
        var signingCert = certCollection.Find(X509FindType.FindByThumbprint, thumbprint, false);
        if (signingCert.Count == 0)
        {
            throw new FileNotFoundException(string.Format("Cert with thumbprint: '{0}' not found in local machine cert store.", thumbprint));
        }

        return signingCert[0];
    }
    finally
    {
        store.Close();
    }
}

3
应该接受这个答案。完美运作!! - Aster Veigas
6
Regex.Replace 应该是 "[^\da-fA-F]" - thumbprints 是十六进制字符串。 - Ross Patterson
谢谢,那个正则表达式刚刚解决了我半小时对着代码咒骂的问题。 - flytzen
很棒的正则表达式来处理那些烦人的秘密隐藏字符... - granadaCoder
正则表达式确实是一个不错的解决方案。然而,我对此进行了一些私人研究,我的证书始终被找到,无论大小写或空格如何。唯一的麻烦制造者可能是“隐形”的部分。 在.NET fw 4.7上测试过。可能与fw版本有关? - Vladimír Hála
没错,这个正则表达式正在移除不可见的部分。 - jhilden

25

我曾经遇到过同样的问题并解决了它:

  1. 我直接将mmc中的指纹复制到VS中。 我比较了字符串,没有发现任何区别。

  2. 通过hash.length检查长度时,有一个差异,41 vs. 40。

在从mmc中复制字符串时,会添加一个不可见字符。


解决方法:

  1. 将指纹从mmc复制到Notepad.exe中
  2. 再次复制此字符串
  3. 粘贴到您的代码中

它可以工作了。


10

我也成为了这个问题的受害者。在Windows控制台中显示的指纹信息中有一个Unicode“从左到右”的字符,而且还包含了小写的十六进制字符,并且每两个字符之间有空格。CertUtil输出结果中也有小写字符和空格。为了得到匹配结果,我必须将findValue指定为一个经过以下转换后的字符串:

  1. 去掉前导特殊字符
  2. 去掉字符簇之间的空格
  3. 将所有字符改为大写

10

我也遇到了这个问题,我编写了以下函数以清理从MMC复制和粘贴的指纹:

public string CleanThumbprint(string mmcThumbprint)
    {
        //replace spaces, non word chars and convert to uppercase
        return Regex.Replace(mmcThumbprint, @"\s|\W", "").ToUpper();
    }

...
        var myThumbprint = CleanThumbprint("‎b3 ab 84 e5 1e e5 e4 75 e7 a5 3e 27 8c 87 9d 2f 05 02 27 56");
        var myCertificate = certificates.Find(X509FindType.FindByThumbprint, myThumbprint, true)[0];

3

这段代码应该可以工作。

我猜你是从证书管理控制台复制了这个指纹。而且复制的值包含不可读的Unicode符号,在Visual Studio中是看不见的。试着删除第一个不可见的符号,如果我所想的是对的话,这个代码就可以正常工作了。


3
我遇到了相同的问题。我在这里找不到答案,所以我会发布它。对于我来说,X509Store find函数似乎根本不起作用。我通过一个简单的for循环和手动检索证书来验证了这一点。
  X509Store store = new X509Store(StoreName.Root,StoreLocation.LocalMachine);
        store.Open(OpenFlags.ReadOnly);
        X509Certificate cert = new X509Certificate();
        for (int i = 0; i < store.Certificates.Count; i++)
        {
            if (store.Certificates[i].SerialNumber == "XXXX")
            {
                cert = store.Certificates[i];
            }
        }

1
这是上述建议的简单代码版本 - 当然,它对我起作用了。
 private X509Certificate2 GetCertificate()
    {
        var certStore = new X509Store("my");
        certStore.Open(OpenFlags.ReadOnly);
        try
        {
            const string thumbprint = "18 33 fe 3a 67 d1 9e 0d f6 1e e5 d5 58 aa 8a 97 8c c4 d8 c3";
            var certCollection = certStore.Certificates.Find(X509FindType.FindByThumbprint,
            Regex.Replace(thumbprint, @"\s+", "").ToUpper(), false);
            if (certCollection.Count > 0)
                return certCollection[0];
        }
        finally
        {
            certStore.Close();
        }
        return null;
    }

1

我也遇到了这个看不见的Unicode字符。尝试使用记事本(Windows 10)并没有很好地解决我的问题。最终,我使用PowerShell获取了干净的指纹十六进制。

PS C:\> $tp= (Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {$_.Subject -match "mycert"}).Thumbprint;
PS C:\> $tp

Unicode字符完了。


1

将以下代码替换为在存储中查找证书的代码:

var results = store.Certificates.Find(findType, findValue, true); 

另外,第三个参数是一个布尔值,只有在证书有效时才返回证书。因此,请确保您的证书是有效的。如果您有自签名证书或类似证书,则只需将第三个参数传递为“false”。


证书有效,因为当我硬编码方法返回1值时。 var results = store.Certificates.Find(findType, "7a6fa503ab57b81d6318a51ca265e739a51ce660", true); //result.Count = 1 :) - nunofamel
你能检查一下运行时传递给方法的指纹 ID 是什么吗? - Rajesh
你把代码中的语法改成上面那个了吗? - Rajesh
我的应用程序中原始代码就像上面那样,这只是一个复制+粘贴错误 :) - nunofamel
你能否更新上面的代码,以便其他人不会认为它是一个错误。同时确保值完全相同。在代码中尝试使用string.compare将硬编码的值与传递到方法中的值进行比较。 - Rajesh
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接