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;

}
Yoda
  • 323
  • 6
  • 14

3 Answers3

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);
}
Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249
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());
Jim Garrison
  • 85,615
  • 20
  • 155
  • 190
-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.

ilooner
  • 2,480
  • 15
  • 32
  • `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. – Jim Garrison Feb 13 '18 at 05:22
  • Thanks for pointing out the bug. Silly mistake on my part. I've updated the answer. – ilooner Feb 13 '18 at 06:00