0
 def hash_string(s):

hsh = bytearray(hashlib.md5(s.encode(encoding="ascii")).digest())
assert len(hsh) == 16
output = \
    int.from_bytes(hsh[0:4], "big") ^ \
    int.from_bytes(hsh[4:8], "big") ^ \
    int.from_bytes(hsh[8:12], "big") ^ \
    int.from_bytes(hsh[12:16], "big")
return binascii.hexlify(output.to_bytes(4, byteorder='big')).decode("ascii")

I want to do the same in Java. However I am stuck because I am not sure how to proceed after creating the hash. Below is my code in java

      private static String hashString(String s) throws NoSuchAlgorithmException {
    MessageDigest md = MessageDigest.getInstance("MD5");
    md.update(s.getBytes());
    byte[] digest = md.digest();

    System.out.println("Length of hash after md5" +digest.length);
    String myHash = DatatypeConverter.printHexBinary(digest).toUpperCase();
    System.out.println("Length of the stirng" +myHash.getBytes().length);
    return myHash;

}

3 Answers 3

1

ByteBuffer has methods that correspond naturally to your code, the equivalent Java looks something like

private static String hashString(String s) 
            throws NoSuchAlgorithmException, UnsupportedEncodingException {
    MessageDigest md = MessageDigest.getInstance("MD5");
    byte[] digest = md.digest(s.getBytes("US-ASCII"));

    byte[] sub1 = Arrays.copyOfRange(digest, 0, 4);
    byte[] sub2 = Arrays.copyOfRange(digest, 4, 8);
    byte[] sub3 = Arrays.copyOfRange(digest, 8, 12);
    byte[] sub4 = Arrays.copyOfRange(digest, 12, 16);
    int x1 = java.nio.ByteBuffer.wrap(sub1).getInt();
    int x2 = java.nio.ByteBuffer.wrap(sub2).getInt();
    int x3 = java.nio.ByteBuffer.wrap(sub3).getInt();
    int x4 = java.nio.ByteBuffer.wrap(sub4).getInt();

    return DatatypeConverter.printHexBinary(java.nio.ByteBuffer.allocate(4)
            .putInt(x1 ^ x2 ^ x3 ^ x4).array());
}

There is a more efficient hex encoder documented on this site; you could add the following and replace the last line above with return bytesToHex(java.nio.ByteBuffer.allocate(4).putInt(x1 ^ x2 ^ x3 ^ x4).array());

private final static char[] hexArray = "0123456789ABCDEF".toCharArray();

public static String bytesToHex(byte[] bytes) {
    char[] hexChars = new char[bytes.length * 2];
    for (int j = 0; j < bytes.length; j++) {
        int v = bytes[j] & 0xFF;
        hexChars[j * 2] = hexArray[v >>> 4];
        hexChars[j * 2 + 1] = hexArray[v & 0x0F];
    }
    return new String(hexChars);
}
Sign up to request clarification or add additional context in comments.

Comments

1

This is does not require ByteBuffer or complicated logic:

    // Precondition: (digest.length % 4) == 0
    byte[] sh = new byte[digest.length/4];
    for (int i=0; i<sh.length; i++)
        for (int j=0; j<digest.length; j+=4)
            sh[i] ^= digest[i+j];

    // Format result as hex
    StringBuilder hex = new StringBuilder();
    Formatter     fmt = new Formatter(hex);
    for (byte b : sh) fmt.format("%02x", b); 
    System.out.println(hex.toString());

Comments

-1

If the hash is 4 bytes you can take two approaches:

To Int To Hex

You can merge the bytes in the digest into an int. Then you can convert the int into a hex string. Note if the bytes in the digest are in little-endian order, then you will have to combine the bytes in reverse order.

int digestInt = ((int)digest[0]) & 0xFF;
digestInt = digestInt << 8 | (((int)digest[1]) & 0xFF);
digestInt = digestInt << 8 | (((int)digest[2]) & 0xFF);
digestInt = digestInt << 8 | (((int)digest[3]) & 0xFF);

Now that you have an integer you can make a hex string by doing Integer.toHexString(digestInt).

To Hex

You can convert each byte into a hex string and append the strings.

public static String encodeBytesToHex(final byte[] bytes)
{
  final StringBuilder sb = new StringBuilder();

  for (int byteIndex = 0; byteIndex < bytes.length; byteIndex++) {
    final String hexByte = Integer.toHexString(((int)bytes[byteIndex]) & 0xFF);
    sb.append(hexByte);
  }

  return sb.toString();
}

The advantage of this is that it will work for a hash of any number of bytes.

2 Comments

digestInt <<= ((int)digest[1]) & 0xFF; This will left-shift digestInt by the number of bits specified in digest[1], which can be (after masking) 0-255. Not what you want, I believe.
Thanks for pointing out the bug. Silly mistake on my part. I've updated the answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.