7

I am currently working on a project that will involve credit card swipes for admissions based on database rows. Like a will call system, the SHA-256 hash of the CC number must match the hash in the DB row in order to be considered the "proper pickup".

However, because the box office system is based in the browser, the CC number on pickup must be hashed client-side, using Javascript, and then compared to the previously downloaded will call data.

However when trying to hash the numbers, the hash always ends up different than what was hashed when the DB row was created (using VB.NET and SQL Server 2008 R2). For example, if a CC number in the database happened to be 4444333322221111, then the resulting hash from .NET would become xU6sVelMEme0N8aEcCKlNl5cG25kl8Mo5pzTowExenM=.

However, when using any SHA-256 hash library for Javascript I could find, the resulting hash would always be NbjuSagE7lHVQzKSZG096bHtQoMLscYAXyuCXX0Wtw0=.

I'm assuming this is some kind of Unicode/UTF-8 issue, but no matter what I try I cannot get the hashes to come out the same and it's starting to drive me crazy. Can anyone offer any advice?

Here's something that may provide some insight. Please go to http://www.insidepro.com/hashes.php?lang=eng and insert "4444333322221111" without quotes into the Password box. Afterwards, scroll down to the SHA-256 section.

You can see that there are four results, two of them are the hash codes I posted (the second from the top being the Javascript hash and the bottom one being the SQL hash). According to that page, the bottom hash result is generated using a base 64 string, as well as making the password into unicode format.

I've investigated this and tried many different functions to encode the password into unicode format, but no matter what little tweaks I try or other functions I make, I could never get it to match the hash code I need.

I am currently investigating the parameters used when calling the SHA-256 function on the server side.

UPDATE:

So just to make sure I wasn't crazy, I ran the Hash method I'm using for the CC numbers in the immediate window while debugging. Again, the result remains the same as before. You can see a screenshot here: https://i.stack.imgur.com/0MhHP.png

William Thomas
  • 2,108
  • 3
  • 22
  • 32
  • Could you provide some information on how you achieved the hash on both ends? What functions, your code, etc ... – JaredPar May 11 '12 at 22:05
  • possible duplicate of [Java SHA256 outputs different hash to PHP SHA256?](http://stackoverflow.com/questions/4680661/java-sha256-outputs-different-hash-to-php-sha256) – Adam Liss May 11 '12 at 22:06
  • Show some code? I am using this SHA512: http://pajhome.org.uk/crypt/md5/sha512.html and I previously used this SHA-1: http://www.movable-type.co.uk/scripts/sha1.html with no C# interop problems. (Related question: why not use sha512?) – Jamie Treworgy May 11 '12 at 22:08
  • @Adam, I don't think this is a duplicate, at least not of the question you linked to. This is Javascript vs. C#. That is PHP vs. Java. It couldn't be more different, since it's almost certainly a usage issue, and the usage could be entirely different across those platforms. – Jamie Treworgy May 11 '12 at 22:11
  • The correct result seems to be that of the javascript function, so the problem must be in the c# code – user570783 May 11 '12 at 22:14
  • 2
    @jamietre Regardless of the specific language, the problem may be one of encoding: do javascript and C# represent the characters as identical byte streams? If not, that would account for the different hashes. – Adam Liss May 11 '12 at 22:21
  • So it es an encoding problem. To be more explicit: the base 64 encoded digest of the ascii/utf-8/latin1 string "4444333322221111" is NbjuSagE7lHVQzKSZG096bHtQoMLscYAXyuCXX0Wtw0=. It think it may be easier to convert the cc number to ascii in the server than to convert the string to unicode (utf-16?) – user570783 May 11 '12 at 22:48
  • utf-8/ascii/latin-1 have the same 16 bytes for the cc numer, but the unicode (utf-16?) string is 32 bytes (2 bytes per character) so the digests are different. – user570783 May 11 '12 at 22:56

3 Answers3

2

According to online SHA-256 hash calculator and a base-64 to hex decoder, it is the .NET implementation that has not calculated the hash correctly. You may want to double check the parameters you pass to the hashing functions.

When you are dealing with two untrusted implementations, it is always a good idea to find another independent implementation, and choose the one that matches the third one as correct. Either that, or find some test vectors, and validate the implementations individually.


EDIT:

A quick experiment shows that the SHA-256 hash you get from .NET matches the hext string 3400340034003400330033003300330032003200320032003100310031003100 - little endian 16-bit characters. Make sure you pass in ASCII.

vhallac
  • 13,301
  • 3
  • 25
  • 36
2

Adam Liss had it right when he mentioned the byte arrays between strings in .NET/SQL Server are different than strings in Javascript. The array in .NET for the string 4444333322221111 would look like [52 0 52 0 52 0 52 0 51 0 51 0... etc.] and the same thing in Javascript would just look like [52 52 52 52 51 51 51 51...]. Thus, with different byte arrays, different hashes were generated.

I was able to remedy this for my application by modifying the base 64 SHA-256 hashing algorithm from here, where each character is pulled from the string one at a time in order to generate the hash.

Rather than having it do it this way, I first converted the string into a unicode-like byte array (like the .NET example above, 52 0 52 0 etc), fed that array to the hashing algorithm instead of the string, and did some very minor tweaks in order for it to grab each array member to generate the hash. Low and behold, it worked and now I have a very convenient method of hashing CC numbers in the same fashion as the .NET framework for quick and easy order lookup.

Community
  • 1
  • 1
William Thomas
  • 2,108
  • 3
  • 22
  • 32
  • 1
    You might have tried `Encoding.ASCII.GetBytes("4444333322221111")` to ensure you were dealing with ASCII on the .NET side. Remember, cryptographic functions deal with bytes, not text. The mapping between text and bytes is not one-to-one, unless you take encoding into account. – Drew Noakes Feb 12 '13 at 15:47
  • William, care to share how you did that? – Jaans Jul 31 '13 at 02:44
0

Are you sure about your JavaScript SHA256 function ?


And your firstly generated hash ?

SHA-256("4444333322221111"); // 35b8ee49a804ee51d5433292646d3de9b1ed42830bb1c6005f2b825d7d16b70d

hex: 35b8ee49a804ee51d5433292646d3de9b1ed42830bb1c6005f2b825d7d16b70d
HEX: 35B8EE49A804EE51D5433292646D3DE9B1ED42830BB1C6005F2B825D7D16B70D
h:e:x: 35:b8:ee:49:a8:04:ee:51:d5:43:32:92:64:6d:3d:e9:b1:ed:42:83:0b:b1:c6:00:5f:2b:82:5d:7d:16:b7:0d
base64: NbjuSagE7lHVQzKSZG096bHtQoMLscYAXyuCXX0Wtw0=
ChristopheCVB
  • 7,269
  • 1
  • 29
  • 54