0

I'm trying to validate the content of an XML node with SHA-1 , basically, we generate an SHA-1 hash with the content of that node and both sides (client C# and server Java) should have exactly the same hash.

The problem is , I have checked with a diff tool the content of both texts and there is not any difference. But I'm getting a different hash than the client.

C# hash : 60-53-58-69-29-EB-53-BD-85-31-79-28-A0-F9-42-B6-DE-1B-A6-0A

Java hash: E79D7E6F2A6F5D776447714D896D4C3A0CBC793

The way the client (C#) is generating the hash is this:

try
    {
        Byte[] stream = null;
        using (System.Security.Cryptography.SHA1CryptoServiceProvider shaProvider = new System.Security.Cryptography.SHA1CryptoServiceProvider())
        {
            stream = shaProvider.ComputeHash(System.Text.Encoding.UTF8.GetBytes(text));
            if (stream == null)
            {
                hash = "Error";
            }
            else
            {
                hash = System.BitConverter.ToString(stream);
            }
        }
    }
    catch (Exception error)
    {
        hash = string.Format("Error SHA-1: {0}", error);
    }
    return hash;

and this is how the server (Java) is generating the hash:

        byte[] key = content.getBytes();


        MessageDigest md = MessageDigest.getInstance("SHA1");

        byte[] hash = md.digest(key);

        String result = "";
        for (byte b : hash) {
            result += Integer.toHexString(b & 255);
        }
        return result.toUpperCase();

can someone help me ? .. thanks :)

UPDATE: In order to check what's going on I have checked other ways to get a SHA1 hash in C# and I found this:

/// <summary>
        /// Compute hash for string encoded as UTF8
        /// </summary>
        /// <param name="s">String to be hashed</param>
        /// <returns>40-character hex string</returns>
        public static string SHA1HashStringForUTF8String(string s)
        {
            byte[] bytes = Encoding.UTF8.GetBytes(s);

            using (var sha1 = SHA1.Create())
            {
                byte[] hashBytes = sha1.ComputeHash(bytes);
                return System.BitConverter.ToString(hashBytes).Replace("-",string.Empty);
            }
        }

This code gives this output:

E79D07E6F2A6F5D776447714D896D4C3A0CBC793

AND !! I just noticed that Python is giving the same output (sorry, I should double checked this)

So this is the deal

Using this provider: System.Security.Cryptography.SHA1CryptoServiceProvider shaProvider = new System.Security.Cryptography.SHA1CryptoServiceProvider()

Is giving a completly different output on three different machines ..

Using the above method in C# gives the same result as python does, also, for some reason Java is giving a sightly different output:

E79D7E6F2A6F5D776447714D896D4C3A0CBC793

Ideas?, is java the problem? the byte to hex method on java is the problem? there is another alternative?

Rafihna
  • 15
  • 7
  • 1
    Can you compare which one, if either is generating the correct hash by putting a dummy string in (like "test") and comparing it with an online hash to see which one is correct... That C# Hash looks off to me but I don't want to post an answer until I find the real problem – Steve Byrne Oct 17 '16 at 17:10
  • I've tried generating a SHA-1 hash with python, assuming that python uses an unsigned byte it seems that our code is correct (in java) , this is the result in python : e79d07e6f2a6f5d776447714d896d4c3a0cbc793 – Rafihna Oct 17 '16 at 17:30
  • 2
    Show outputs for your Java code and your C# code using the same test input. – erickson Oct 17 '16 at 17:40
  • 1
    Just a quick tip. Have you checked what chars are used for linebreaks? As I found out in my similar question [here](http://stackoverflow.com/questions/39518616/converting-sha256-from-java-to-c-sharp) the linebreaks may be represented by different chars in Java and C# which won't give any visible change, but it will affect any hash value – Shazi Oct 17 '16 at 17:51
  • thanks! to avoid the use of \n or any linebreak character I use a xslt with the xsl:text tags, I've checked the preview with the debugger of both platforms and both are representing the linebreak with a \n . – Rafihna Oct 17 '16 at 18:01
  • I've just updated my question – Rafihna Oct 17 '16 at 19:58
  • Any luck or changes since yesterday? – Steve Byrne Oct 18 '16 at 22:06
  • yeah, just as @Shazi said, I checked how c# was generating the linebreaks and that was the problem.. now it's solved .. thanks guys! – Rafihna Oct 19 '16 at 17:04
  • Suspected as much, Glad to have helped :) – Shazi Oct 19 '16 at 17:45

2 Answers2

1

Your problem is that you're not hashing the same bytes in both API.

If you choose to modify java's version, it should look like this:

byte[] key = content.getBytes("UTF8");
[...]

If you choose to modify c#' version, it should look like this:

stream = shaProvider.ComputeHash(System.Text.Encoding.UTF16.GetBytes(text));
[...]

Either way, both api should get the key's bytes through the same encoding.

Jazzwave06
  • 1,883
  • 1
  • 11
  • 19
  • hi and thanks for your answer , the content (in java) is being encoded with UTF8, so adding the encoding to the getBytes method didn't work. – Rafihna Oct 17 '16 at 17:11
  • @Rafihna The content in-memory (in the String `content`) is not encoded at all. Only if you convert it to bytes an encoded is applied (in your code the Java default encoding). – Robert Oct 17 '16 at 17:19
  • thanks! I've already changed the code with your example but I'm getting the same output, I'm guessing that the client ( I have not control on the client code) is generating the hash on a different way from us – Rafihna Oct 17 '16 at 17:32
  • Well simply make sure that the input bytes are the same in both client. Troubleshoot from there. If the input bytes are not the same, the hash will never be the same. Since SHA1 implementation are given in both frameworks, I doubt the problem lie with the hash implementation. – Jazzwave06 Oct 17 '16 at 17:33
1

Try using this as your hashing in C#:

static string Hash(string input)
    {
        using (SHA1Managed sha1 = new SHA1Managed())
        {
            var hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(input));
            var sb = new StringBuilder(hash.Length * 2);

            foreach (byte b in hash)
            {
                // can be "x2" if you want lowercase
                sb.Append(b.ToString("x2"));
            }

            return sb.ToString();
        }
    }
Hash("test"); //a94a8fe5ccb19ba61c4c0873d391e987982fbbd3

And then use this as your Java hashing:

private static String convertToHex(byte[] data) {
    StringBuilder buf = new StringBuilder();
    for (byte b : data) {
        int halfbyte = (b >>> 4) & 0x0F;
        int two_halfs = 0;
        do {
            buf.append((0 <= halfbyte) && (halfbyte <= 9) ? (char) ('0' + halfbyte) : (char) ('a' + (halfbyte - 10)));
            halfbyte = b & 0x0F;
        } while (two_halfs++ < 1);
    }
    return buf.toString();
}

public static String SHA1(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException {
    MessageDigest md = MessageDigest.getInstance("SHA-1");
    byte[] textBytes = text.getBytes("iso-8859-1");
    md.update(textBytes, 0, textBytes.length);
    byte[] sha1hash = md.digest();
    return convertToHex(sha1hash);
}
SHA1("test"); //a94a8fe5ccb19ba61c4c0873d391e987982fbbd3

Note you need the following imports:

import java.io.UnsupportedEncodingException; import
java.security.MessageDigest; import
java.security.NoSuchAlgorithmException;

Throws declarations are option, adjust to best fit your code!

Steve Byrne
  • 1,320
  • 1
  • 16
  • 29
  • I updated my answer to include the Java code and the C# code, my test cases worked, if you have problems using those sets of code there is a difference with the string you are passing the two functions, best of luck! Test case: Java("test") = a94a8fe5ccb19ba61c4c0873d391e987982fbbd3 C#("test") = a94a8fe5ccb19ba61c4c0873d391e987982fbbd3 – Steve Byrne Oct 17 '16 at 21:28
  • I used your hashing method for java... thanks for your time! – Rafihna Oct 19 '16 at 17:06