27

I have the following C# code (from a library I'm using) that tries to find a certificate comparing the thumbprint. Notice that in the following code both mycert.Thumbprint and certificateThumbprint are strings.

var certificateThumbprint = AppSettings.CertificateThumbprint;

var cert =
    myStore.Certificates.OfType<X509Certificate2>().FirstOrDefault(
      mycert => 
      mycert.Thumbprint != null && mycert.Thumbprint.Equals(certificateThumbprint)
      );

This fails to find the certificate with the thumbprint because mycert.Thumbprint.Equals(certificateThumbprint) is false even when the strings are equal. mycert.Thumbprint == certificateThumbprint also returns false, while mycert.Thumbprint.CompareTo(certificateThumbprint) returns 0.

enter image description here

I might be missing something obvious, but I can't figure out why the Equals method is failing. Ideas?

Marius Bancila
  • 16,053
  • 9
  • 49
  • 91
  • 15
    This might be a strange question... but are the Lengths the same on each string? (trying to see if there are any non-printable characters in there somehow). – vcsjones Sep 23 '14 at 15:30
  • 5
    From the [`string.CompareTo`](http://msdn.microsoft.com/en-us/library/fkw3h78a(v=vs.110).aspx) docs: *Character sets **include ignorable characters.** The CompareTo method does not consider such characters when it performs a culture-sensitive comparison. For example, if the following code is run on the .NET Framework 4 or later, a comparison of "animal" with "ani-mal" (using a soft hyphen, or U+00AD) indicates that the two strings are equivalent.* – Yuval Itzchakov Sep 23 '14 at 15:33
  • 2
    @Yuval Your citation seems to imply the opposite of what vcsjones is suspecting. – Jonathon Reinhart Sep 23 '14 at 15:35
  • 3
    @JonathonReinhart My citation indicates that `CompareTo` excludes ignorable charecters from the comparison. That is why `CompareTo` returns 0. I think that's what vcsjones ment. – Yuval Itzchakov Sep 23 '14 at 15:36
  • First do what @vcsjones suggests. If everything checks out, try normalizing both operands, e.g, something akin to `string.Equals(mycert.Thumbprint != null ? mycert.Thumbprint.Normalize() : null, certificateThumbprint.Normalize())`. – Mike Strobel Sep 23 '14 at 15:37
  • 2
    Can you give short example that reproduct the same problem? That we can test ofc. – KugBuBu Sep 23 '14 at 15:38
  • 2
    I suggest for troubleshooting: Loop through the strings and do a char by char comparison. – Postlagerkarte Sep 23 '14 at 15:50
  • 4
    Please look at the `.ToCharArray()` of each string and compare them character-by-character. There is almost certainly a subtle character difference. If the strings are reporting: *the strings probably aren't equal* – Marc Gravell Sep 23 '14 at 15:52
  • 1
    Is this a LINQ to SQL/Entity Framework query? – Troy Carlson Sep 23 '14 at 15:56
  • Not the problem, but if you're using `FirstOrDefault`, you should be checking `myCert` for null in addition to `myCert.Thumbprint`. – Peter Ritchie Sep 23 '14 at 16:17
  • @PeterRitchie His delegate is the predicate that gets passed to `FirstOrDefault()`; it's not operating on the result. The input sequence cannot contain any null elements due to the presence of the `OfType()` operator. The result (`cert`) might be null, but not `myCert`. – Mike Strobel Sep 23 '14 at 16:20
  • 1
    Did you copy paste the cert thumbprint into your AppSettings? See [How to find certificate by its thumbprint in C#](http://stackoverflow.com/questions/11115511/how-to-find-certificate-by-its-thumbprint-in-c-sharp). – CodeCaster Sep 23 '14 at 16:21
  • @MikeStrobel yeah, that's true :$ – Peter Ritchie Sep 23 '14 at 16:21
  • The Thumbprint is hex-encoded binary data, there should be no ignorable/unprintable characters in it. – Peter Ritchie Sep 23 '14 at 16:23
  • 4
    @PeterRitchie the first line of code reads `var certificateThumbprint = AppSettings.CertificateThumbprint;`, hence my question. Problem is explained [there](http://stackoverflow.com/questions/11115511/how-to-find-certificate-by-its-thumbprint-in-c-sharp). And don't you love hit'n'run-questions, OP wasn't seen for an hour already. – CodeCaster Sep 23 '14 at 16:24
  • Could be a compiler issue? Maybe the "equals" is not the method of the string but of the object – Davide Lettieri Sep 23 '14 at 18:07
  • 1
    Thanks for the helpful answers. It was indeed a problem with the string from the app settings. It started with two characters 8206 (or U+2000E, i.e. left-to-right-mark). I have fixed that and everything worked. – Marius Bancila Sep 23 '14 at 20:48

3 Answers3

22

CompareTo ignores certain characters:

static void Main(string[] args)
{
    var a = "asdas"+(char)847;//add a hidden character
    var b = "asdas";
    Console.WriteLine(a.Equals(b)); //false
    Console.WriteLine(a.CompareTo(b)); //0
    Console.WriteLine(a.Length); //6
    Console.WriteLine(b.Length); //5

   //watch window shows both a and b as "asdas"
}

(Here, the character added to a is U+034F, Combining Grapheme Joiner.)

Debug mode

So CompareTo's result is not a good indicator of a bug in Equals. The most likely reason of your problem is hidden characters. You can check the lengths to be sure.

See this for more info.

brz
  • 5,926
  • 1
  • 18
  • 18
  • Indeed, I was having two U+200E (left-to-right mark) characters at the beginning of the `certificateThumbprint` string. Removing that fixed the problem. – Marius Bancila Sep 23 '14 at 20:49
  • Of all the answers this is the correct one. There are also comments that also indicated the correct problem, including linking to this question http://stackoverflow.com/questions/11115511/how-to-find-certificate-by-its-thumbprint-in-c-sharp. I will mark this as the accepted answer. Wish I could also do that for comments. – Marius Bancila Sep 23 '14 at 20:54
  • has this exact case as a result of &nbsp was stored in a string in the database but was only showing as a space, was able to see the unicode chars when pasted into sublime text fkn unicode encodings can be such a nuisance sometimes lol – d0rf47 Aug 17 '22 at 16:38
2

You may wish to try using an overload of String.Equals that accepts a parameter of type StringComparison.

For example:

myCert.Thumbprint.Equals(certificateThumbprint, StringComparison.[SomeEnumeration])


Where [SomeEnumeration] is replaced with one of the following enumerated constants:

 - CurrentCulture
 - CurrentCultureIgnoreCase
 - InvariantCulture
 - InvariantCultureIgnoreCase
 - Ordinal
 - OrdinalIgnoreCase


Reference the MSDN Documentation found here. enter image description here

AperioOculus
  • 6,641
  • 4
  • 26
  • 37
0

Sometimes when we insert data in database it stores some spaces like "question ". And when you will try to compare it with "question" it returns false. So my suggestion is: please check the value in database or use Trim() method.

In your case, please try: mycert.Thumbprint != null && mycert.Thumbprint.trim().equals(certificateThumbprint.trim())

I think it will return true if any record will exist.

Subhash
  • 51
  • 2