1

HashCalc has, at the top, a field called "Data Format" I switch that to "HexString" then type in test value of 9a in the data field. I run a SHA-1 hash on it and the answer is:13cba177bcfad90e7b3de70616b2e54ba4bb107f

(NOTE: online hashers will hash "9a" as a string resulting in an answer of e8eef065fb7295044d65b305bab18a9a645d1abf. Which is wrong for this application)

Now, I need to embed this type of hashing into my Java program. This is what I got so far (wrapped in try/catch):

String ss = "9a";
ByteBuffer bb = ByteBuffer.allocate(8);  
byte[] ba = bb.putLong(Long.decode("0x"+ss).longValue()).array();
MessageDigest md = MessageDigest.getInstance("SHA-1");
String results = encodeHex(md.digest(ba));
System.out.println("sha:"+results);

However, my result is E73C417858807239DD5BC30BA978C14D57F80834

What am I doing wrong?

EDIT: Added Hex tag back, it seems obvious that the data has to be in some sort of hex format. As HashCalc has to be set to "HexString" and not "TextString" which returns a different result. And the solution could well include a change to how Im dealing with these hex numbers. --> which turned out to be true

Pimp Trizkit
  • 19,142
  • 5
  • 25
  • 39
  • Probably this might help http://stackoverflow.com/a/6706816/655756 – n1ckolas Mar 27 '13 at 21:24
  • Unfortunately, that answer makes "almost" no sense. As when i put `aff` into HashCalc it gives me an error of "The length of the hex string must be even!" as it takes two hex digits to represent one byte. Plus, im not sure how to add that library to my NetBeans! DOH! – Pimp Trizkit Mar 27 '13 at 21:28
  • 2
    Maybe hashing the 8-byte 000000000000009a is different from hashing the one byte 9a? – Louis Wasserman Mar 27 '13 at 21:29
  • btw Why you think `e8eef065fb7295044d65b305bab18a9a645d1abf` is wrong? It is the correct hash... – informatik01 Mar 27 '13 at 21:35
  • 1
    `e8eef065fb7295044d65b305bab18a9a645d1abf` is the correct hash of `9a` as a STRING not a hex number.... use HashCalc (link in question above) to test. It has two options "Text String" and "Hex String". I need to duplicate the result of "Hex String" not "Text String" which is `e8eef065fb7295044d65b305bab18a9a645d1abf` – Pimp Trizkit Mar 27 '13 at 21:39
  • Hmm, I see. Kind of weird... – informatik01 Mar 27 '13 at 21:49
  • 1
    @LouisWasserman - That was the turning point. See my answer. – Pimp Trizkit Mar 28 '13 at 01:02

3 Answers3

3

DISCLAIMER

The OP (Pimp Trizkit), is the one who found the right solution. I just used his solution (with minor change) to showcase the result and to have fun. All rights reserved)

Also, the bytes array to hex string converting algorithm offered by the OP is much faster, than the ones in my example code. See his solution for the implementation.
(For the details read the comments down below)


One of the manual solutions:
(Important! It was my first answer, but it's only for getting hash from the text string, not from the hex string as the OP asked. See the update below):

import java.security.MessageDigest;

public class TestHash {

    public static void main(String[] args) throws Exception {
        String password = "9a";

        MessageDigest md = MessageDigest.getInstance("SHA-1");
        md.update(password.getBytes());
        byte[] byteData = md.digest();
        // byte[] byteData = md.digest(password.getBytes());    // both updates and completes the hash computation

        // Method 1 of converting bytes to hex format
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < byteData.length; i++) {
            sb.append(Integer.toString((byteData[i] & 0xFF) + 0x100, 16).substring(1));
        }

        System.out.println("1) Hex format : " + sb.toString());

        // Method 2 of converting bytes to hex format
        StringBuffer hexString = new StringBuffer();
        for (int i = 0; i < byteData.length; i++) {
            String hex = Integer.toHexString(0xff & byteData[i]);
            // NB! E.g.: Integer.toHexString(0x0C) will return "C", not "0C"            
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        System.out.println("2) Hex format : " + hexString.toString());      
    }
}

Output:

1) Hex format : e8eef065fb7295044d65b305bab18a9a645d1abf
2) Hex format : e8eef065fb7295044d65b305bab18a9a645d1abf

UPDATE

Based on the OP found solution, here is the code, that showcases getting the SHA-1 hash from a hex string instead of a text string. Plus it presents several methods of manually converting byte arrays to hex string (just for fun). Sorry, I was in a mood ))

See my comments inside the main method and inside different bytesToHexString() methods for the explanation of important moments.

import java.security.MessageDigest;
import java.math.BigInteger;
import javax.xml.bind.DatatypeConverter;

public class TestHash3 {

    public static void main(String[] args) throws Exception {
        String hexString = "1234";

        /*
         * NB!
         * Before passing hex string to DatatypeConverter.parseHexBinary(),
         * we need to check if the hex sting is even-length, 
         * otherwise DatatypeConverter.parseHexBinary() will throw a
         * java.lang.IllegalArgumentException: hexBinary needs to be even-length
         */
        hexString = (hexString.length() % 2 == 0) ? hexString : "0" + hexString;
        byte[] bytes = DatatypeConverter.parseHexBinary(hexString);

        MessageDigest md = MessageDigest.getInstance("SHA-1");
        byte[] byteData = md.digest(bytes);

        System.out.println("1) SHA-1 hash for the hex string " + hexString + ": " +
                            bytesToHexString1(byteData));
        System.out.println("2) SHA-1 hash for the hex string " + hexString + ": " +
                            bytesToHexString2(byteData));
        System.out.println("3) SHA-1 hash for the hex string " + hexString + ": " +
                            bytesToHexString3(byteData));
        System.out.println("4) SHA-1 hash for the hex string " + hexString + ": " +
                            bytesToHexString4(byteData));
    }

    public static String bytesToHexString1(byte[] bytes) {
        StringBuffer hexBuffer = new StringBuffer();
        for (int i = 0; i < bytes.length; i++) {
            hexBuffer.append(Integer.toString((bytes[i] & 0xFF) + 0x100, 16).substring(1));
        }

        return hexBuffer.toString();
    }

    public static String bytesToHexString2(byte[] bytes) {
        StringBuffer hexBuffer = new StringBuffer(bytes.length * 2);
        for (byte b: bytes) {
            int n = b & 0xFF;   // casting to integer to avoid problems with negative bytes
            if (n < 0x10) {
                hexBuffer.append("0");
            }
            hexBuffer.append(Integer.toHexString(n));
        }

        return hexBuffer.toString();
    }       

    public static String bytesToHexString3(byte[] bytes) {
        StringBuffer hexBuffer = new StringBuffer();
        for (int i = 0; i < bytes.length; i++) {
            String hexString = Integer.toHexString(0xff & bytes[i]);
            // NB! E.g.: Integer.toHexString(0x0C) will return "C", not "0C"            
            if (hexString.length() == 1) {
                hexBuffer.append('0');
            }
            hexBuffer.append(hexString);
        }

        return hexBuffer.toString();
    }

    public static String bytesToHexString4(byte[] bytes) {
        String hexString = new BigInteger(1, bytes).toString(16);

        /*
         * NB!
         * We need an even-length hex string to propely represent bytes in hexadecimal.
         * A hexadecimal representation of one byte consists of two hex digits.
         * If the value is less than 16 (dec), it is prepended with zero
         * E.g.:
         * 1  (byte)    ==> 01 (hex)    // pay attention to the prepended zero
         * 15 (byte)    ==> 0F (hex)
         * 16 (byte)    ==> 10 (hex)    // no need to prepend
         * 255(byte)    ==> FF (hex)
         *
         * BigInteger.toString(16) can return both even and odd-length hex strings.
         * E.g.:
         * byte[] bytes = {15, 16}  // two bytes
         * BigInteger(1, bytes).toString(16) will produce (NB!): f10
         * But we need (NB!): 0f10
         * So we must check if the resulting hex string is even-length,
         * and if not, prepend it with zero.
         */
        return ((hexString.length() % 2 == 0) ? hexString : "0" + hexString);
    }
}

Output:

1) SHA-1 hash for the hex string 1234: ffa76d854a2969e7b9d83868d455512fce0fd74d
2) SHA-1 hash for the hex string 1234: ffa76d854a2969e7b9d83868d455512fce0fd74d
3) SHA-1 hash for the hex string 1234: ffa76d854a2969e7b9d83868d455512fce0fd74d
4) SHA-1 hash for the hex string 1234: ffa76d854a2969e7b9d83868d455512fce0fd74d


btw Checking if the hex string is even-length inside byteToHexString4() so it could be used independently.


UPDATE 2

The user @kan brought one more method of converting byte array to hex string, very simple one liner and the second fastest after the OP's method.

DatatypeConverter.printHexBinary(byte[] val)

Community
  • 1
  • 1
informatik01
  • 16,038
  • 10
  • 74
  • 104
  • `13cba177bcfad90e7b3de70616b2e54ba4bb107f` is the correct hash when you use HashCalc set to "HexString". If you set HashCalc to "TextString" then you are correct. – Pimp Trizkit Mar 27 '13 at 22:36
  • This doesnt work for the `1234` test case mentioned in my original question. But, thanks for all your effort. I finally just figured it all out. – Pimp Trizkit Mar 28 '13 at 00:55
  • @PimpTrizkit OK, you're welcome! Didn't thoroughly checked the solution, just got the first desirable result. Sorry ) – informatik01 Mar 28 '13 at 10:08
  • +1 for all your work and finally coming to a solution that works in my application. Even if it is just a copy of my solution. lol! But, I knew about the exception, and I figured instead of arbitrarily adding a zero to make it even, I decided the exception needed to be pushed back out instead (hence, why my solution throws the exception and doesnt "fix" the number). Because, the HexString has to be even. If its not, the user of my function needs to figure out what they want to do incase the data is odd length. Adding zero might not be the solution. (They might need to request data entry again) – Pimp Trizkit Mar 28 '13 at 15:11
  • i.e. `DatatypeConverter.parseHexBinary()` doesnt "fix" the length either.. for a reason. – Pimp Trizkit Mar 28 '13 at 15:14
  • A little speed test here (as speed is important in this application): `start = new Date().getTime(); for ( i = 0 ; i < 1000000 ; ++i ) bytesToHexString1(byteData); end = new Date().getTime();` for each of the five ways to convert mentioned. Your four ways and my one. Here are the results: `1) bytesToHexString1 time is 4625 2) bytesToHexString2 time is 3110 3) bytesToHexString3 time is 3000 4) bytesToHexString4 time is 5343 5) byteArrayToHexString time is 360` – Pimp Trizkit Mar 28 '13 at 15:28
  • as you can see your fastest method is ~8.3 times slower than the one I picked. So, IMHO, there is no need to spread the other ones. An interesting point to note, your shortest solution (code length) is the longest in time. – Pimp Trizkit Mar 28 '13 at 15:31
  • Also, and unimportant note, NetBeans gives me a warning that I should use `StringBuilder` instead of `StringBuffer`. Tho, I dont think this would speed it up much. There are just too many function calls in those methods. Whereas the one I picked has none. Its all just array manipulations. – Pimp Trizkit Mar 28 '13 at 15:40
  • @PimpTrizkit Wow, I see you also just couldn't stop experimenting )) **Thank you** for sharing your measurements. Of course, you offered a faster algorithm: just array manipulation, constant access time O(1) etc. And you are right - `StringBuilder` in this case insignificantly improved speed. Although again, NetBeans is right, if no synchronization required, `StringBuilder` is the way (I copied the examples from my old code). And I just wrote different solutions for fun. I'll update my answer to point out that **the algorithm you offered is much faster**. – informatik01 Mar 28 '13 at 16:22
  • @PimpTrizkit Updated my answer with the disclaimer) – informatik01 Mar 28 '13 at 16:32
  • 1
    HAHA, nice! Thanks for the credit! Tho, I would really like to credit Louis and Kan for pointing me in the right direction. I just pieced their ideas together. And, as I said before, I didnt write that `byteArrayToHexString`. I found some question about it a while back and they did speed tests and found this one to be probably the fastest. Since speed is important in my application, I had to speed test it all anyhow. – Pimp Trizkit Mar 28 '13 at 16:37
  • @PimpTrizkit Well, everybody will see your solution, where you give credit to them and stuff, so it's OK) – informatik01 Mar 28 '13 at 16:38
  • 1
    No doubt, I would give you another +1 for all this entertaining dialog! (ok, that proves im a geek, talk about programming is interesting??? what?) – Pimp Trizkit Mar 28 '13 at 16:40
  • @PimpTrizkit It was a pleasure for me as well. And yeah, Stack Overflow **is** the place for all geeks! – informatik01 Mar 28 '13 at 16:45
  • 1
    +1 for extended discussions! Haha, ya, I tell everyone its like FaceBook for programmers. (but 10^234 times better... FaceBook makes problems.... here, we solve them) – Pimp Trizkit Mar 28 '13 at 16:46
  • 1
    @PimpTrizkit You're right. And sorry, [**Mark**](http://en.wikipedia.org/wiki/Mark_Zuckerberg) – informatik01 Mar 28 '13 at 16:48
  • 1
    You missed simplest bytesToHexString way - `DatatypeConverter.printHexBinary`. – kan Mar 28 '13 at 20:04
  • @kan Sorry, could you elaborate on that. – informatik01 Mar 28 '13 at 20:18
  • @kan Oh, I see, thank you. It was in no way the exhaustive list of possible bytes to hex methods. Just few of them. But thank you for bringing one more method. And yes, I didn't know of that ) – informatik01 Mar 28 '13 at 20:25
  • @kan Wow, I've just tested `DatatypeConverter.printHexBinary` and not only it the shortest way (at the moment), but it turned out to be **second fastest** after the method offered by the OP. If you don't mind I'll mention it also in my answer with the credit to you. – informatik01 Mar 28 '13 at 20:38
  • No time data to look at for us? – Pimp Trizkit Mar 28 '13 at 22:03
  • Also, without seeing the contents of the `printHexBinary` function. You cant really tell if its the shortest in code length. Just because its in a library doesnt make it count as one line...lol. Because if you look at it that way.. all of our solutions are just one line! But, I see your point, a user only needs to add one `import` and this one line to get it to work. Instead of pasting a whole function. – Pimp Trizkit Mar 28 '13 at 22:17
  • @PimpTrizkit Yes, I meant that it the shortest, because, as you said, it's already in the API. As for the time data, here you go: Time for bytesToHexString1: 4124, Time for bytesToHexString2: 2448, Time for bytesToHexString3: 2633, Time for bytesToHexString4: 5445, Time for bytesToHexString5: 318, Time for bytesToHexString6: 376. Your algorithm is the fastest (bytesToHexString5), and the one, offered by kan (bytesToHexString6) is the second fastest. Of course, each run gives a bit different time, but the results are same. Measured using `System.currentTimeMillis`. – informatik01 Mar 29 '13 at 03:31
  • Nice, and its so close that its practically the same. Thanks. – Pimp Trizkit Mar 29 '13 at 03:36
  • @PimpTrizkit btw Considering the fact, that you figured out the answer for your own question yourself, you should definitely accept your own answer. – informatik01 Mar 29 '13 at 03:38
1

I use a small utility class:

public abstract class Sha1Util
{
    private static final Charset UTF8 = Charset.forName("UTF-8");

    public static MessageDigest newSha1Digest()
    {
        try
        {
            return MessageDigest.getInstance("SHA-1");
        } catch (NoSuchAlgorithmException e)
        {
            throw new Error(e);
        }
    }

    public static void update(final MessageDigest digest, final String s)
    {
        digest.update(s.getBytes(UTF8));
    }

    public static String sha1sum(final MessageDigest digest)
    {
        return String.format("%040x", new BigInteger(1, digest.digest()));
    }
}

Test:

@Test
public void testSha1For9a()
{
    final MessageDigest md = SecUtil.newSha1Digest();
    SecUtil.update(md, "9a");// you could use several updates e.g. for salted passwords
    assertEquals("e8eef065fb7295044d65b305bab18a9a645d1abf", SecUtil.sha1sum(md));

}
kan
  • 28,279
  • 7
  • 71
  • 101
  • 1
    `13cba177bcfad90e7b3de70616b2e54ba4bb107f` is the correct hash when you use HashCalc set to "HexString". If you set HashCalc to "TextString" then you are correct. This is not going to be used in a password/security environment. – Pimp Trizkit Mar 27 '13 at 22:45
  • 2
    @PimpTrizkit When just use `md.update(new byte[]{-102})`. If you need decode hexadecimal string to the byte array use `DatatypeConverter.parseHexBinary("9a")` – kan Mar 27 '13 at 23:41
  • Thanks for that! I figured out my problem, and your comment here about the `DatatypeConverter` was apparently all I needed! However, your actual answer is more of a demonstration of sha-1 hashing.. in general.. rather than a solution. – Pimp Trizkit Mar 28 '13 at 00:58
1

I figured it out! Thanks to Louis Wasserman for inspiration in his comment to my question. When I used 000000000000009a with HashCalc it returned the same result as my function! I realized that I was forcing a certain data length (the length of a long). Turns out it needs to be an arbitrary length (like HashCalc... as I said, I needed to duplicate the HashCalc behavior, not just for 9a or what ever small test case we use, but for all possible data). Even longer than long. So these other solutions don't work here.

I got around this by converting the original hex string to a char[] array then concat them together in pairs and converting to a byte, in a loop, storing each in the byte[] that is passed to digest. This worked! Then I saw Kan's comment to his answer on this, and showed me I was just reinventing the wheel there.

So here is the final code, and its amazingly the same size as the original:

private String HexToSHA1(String ss) throws IllegalArgumentException {
    MessageDigest md = null;
    try { md = MessageDigest.getInstance("SHA-1"); }
    catch ( Exception e ) {}
    return byteArrayToHexString(md.digest(DatatypeConverter.parseHexBinary(ss)));
}

This runs pretty quick, not sure if there is even a faster way. Of course thats also based on how fast your byteArrayToHexString function is. Here is mine:

private String byteArrayToHexString(byte[] data) {
    char[] toDigits =  {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
    int l = data.length;
    char[] out = new char[l << 1];
    for (int i = 0, j = 0; i < l; i++) {
        out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
        out[j++] = toDigits[0x0F & data[i]];
    }
    return new String(out);
}

And its pretty fast. Credit to someone somewhere. I didn't write it.

EDIT: Moving char[] toDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; to the global (or "up one in scope") makes it faster still.

Pimp Trizkit
  • 19,142
  • 5
  • 25
  • 39
  • +1 for figuring out the solution. But there is still one issue with it. It will throw `IllegalArgumentException` if you pass odd-length hex string to `DatatypeConverter.parseHexBinary()` method. See my second update for the improved solution along with several manual methods of converting byte array to hex string (sorry, couldn't help it) – informatik01 Mar 28 '13 at 12:06
  • That is not an issue actually. – Pimp Trizkit Mar 28 '13 at 15:51