56

I'm trying to get the SHA256 of a string in Android.

Here is the PHP code that I want to match:

echo bin2hex(mhash(MHASH_SHA256,"asdf"));
//outputs "f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13b"

Now, in Java, I'm trying to do the following:

            String password="asdf"
            MessageDigest digest=null;
    try {
        digest = MessageDigest.getInstance("SHA-256");
    } catch (NoSuchAlgorithmException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    }
       digest.reset();
       try {
        Log.i("Eamorr",digest.digest(password.getBytes("UTF-8")).toString());
    } catch (UnsupportedEncodingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

But this prints out: "a42yzk3axdv3k4yh98g8"

What did I do wrong here?


Solution thanks to erickson:

 Log.i("Eamorr",bin2hex(getHash("asdf")));

 public byte[] getHash(String password) {
       MessageDigest digest=null;
    try {
        digest = MessageDigest.getInstance("SHA-256");
    } catch (NoSuchAlgorithmException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    }
       digest.reset();
       return digest.digest(password.getBytes());
 }
static String bin2hex(byte[] data) {
    return String.format("%0" + (data.length*2) + "X", new BigInteger(1, data));
}
Paŭlo Ebermann
  • 73,284
  • 20
  • 146
  • 210
Eamorr
  • 9,872
  • 34
  • 125
  • 209
  • 1
    I think your problem may be the getBytes(UTF-8). Try just getBytes(). – Nicholas Aug 23 '11 at 19:07
  • 5
    Why should anyone ever use `getBytes()` without specifying the encoding? – Roland Illig Aug 23 '11 at 19:15
  • 5
    @Eamorr: Could you put the solution in an answer instead of the question, please? – Paŭlo Ebermann Aug 23 '11 at 19:17
  • The code you have as the solution does not work... any help? – EGHDK Mar 14 '12 at 21:06
  • 1
    A clean answer is written up here: http://stackoverflow.com/questions/9661008/compute-sha256-hash-in-android-java-and-c-sharp – ThomasW Jan 31 '14 at 07:42
  • How can convert output back to asdf back in java. can i do that or not due to aha-256 alto ? Thanks – Zeeshan Aug 11 '14 at 11:15
  • @ShanXeeshi, you are correct. These hashes are a one-way encoding of data, it cannot be (easily) reversed to recreate the input. Unless you meant going from byte[] back to String then that's `new String(inputBytes, StandardCharsets.UTF_8);` or similar (you need to know the encoding ahead of time.) – indivisible Feb 21 '17 at 13:02

4 Answers4

34

The PHP function bin2hex means that it takes a string of bytes and encodes it as a hexadecimal number.

In the Java code, you are trying to take a bunch of random bytes and decode them as a string using your platform's default character encoding. That isn't going to work, and if it did, it wouldn't produce the same results.

Here's a quick-and-dirty binary-to-hex conversion for Java:

static String bin2hex(byte[] data) {
    StringBuilder hex = new StringBuilder(data.length * 2);
    for (byte b : data)
        hex.append(String.format("%02x", b & 0xFF));
    return hex.toString();
}

This is quick to write, not necessarily quick to execute. If you are doing a lot of these, you should rewrite the function with a faster implementation.

erickson
  • 265,237
  • 58
  • 395
  • 493
  • Actually, new BigInteger(1, data).toString(16) is all you need in the case of data from a digest function, because it's guaranteed that no nibble will be full of 0s. – class stacker Dec 22 '12 at 12:26
  • @ClassStacker What's the SHA-1 hash of 0x35326b9705d136d0d1b5efa92d440f3171f1b711? – erickson Dec 23 '12 at 08:44
  • @erickson I'm definitely to be blamed for claiming no _nibble_ would ever be zero anyway, but here you go; according to python it's \x00P\xd5\rv\xc8\x15u\x84*\xf3\x1d\xc3L\x9b\x14{\xedX? and I stand here guilty. Thanks for pointing this out. – class stacker Dec 23 '12 at 10:00
  • 2
    @ClassStacker No problem. SHA-1 and SHA-2 hashes, and I assume any cryptographic digest, can produce zero nybbles and bytes, including leading zeros. I didn't expect otherwise, but when you said it was impossible, it didn't take long to verify my assumption. – erickson Dec 24 '12 at 01:43
  • @erickson That may not be a problem for you and me, but I presume it has the potential to lead to occasional trouble elsewhere. ;) – class stacker Dec 24 '12 at 11:07
27

You are along the right lines, but converting the bytes is a little more complicated. This works on my device:

// utility function
    private static String bytesToHexString(byte[] bytes) {
        // http://stackoverflow.com/questions/332079
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(0xFF & bytes[i]);
            if (hex.length() == 1) {
                sb.append('0');
            }
            sb.append(hex);
        }
        return sb.toString();
    }

// generate a hash

    String password="asdf";
    MessageDigest digest=null;
    String hash;
    try {
        digest = MessageDigest.getInstance("SHA-256");
        digest.update(password.getBytes());

        hash = bytesToHexString(digest.digest());

        Log.i("Eamorr", "result is " + hash);
    } catch (NoSuchAlgorithmException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    }

Source: bytesToHexString function is from the IOSched project.

David Snabel-Caunt
  • 57,804
  • 13
  • 114
  • 132
  • 4
    Careful: the bytesToHexString function you took from IOSched project returns the String in lower-case format. When chaining it by doing SHA256(SHA256...(SHA256(input)...) you would get different results from implementations that return the String in upper-case format. This can easily be addressed by chainging the responsible line to `String hex = Integer.toHexString(0xFF & bytes[i]).toUpperCase();` – Anton Kaiser Mar 03 '13 at 20:18
8

Complete answer with use example, thanks to erickson.

public static String getSha256Hash(String password) {
    try {
        MessageDigest digest = null;
        try {
            digest = MessageDigest.getInstance("SHA-256");
        } catch (NoSuchAlgorithmException e1) {
            e1.printStackTrace();
        }
        digest.reset();
        return bin2hex(digest.digest(password.getBytes()));
    } catch (Exception ignored) {
        return null;
    }
}

private static String bin2hex(byte[] data) {
    StringBuilder hex = new StringBuilder(data.length * 2);
    for (byte b : data)
        hex.append(String.format("%02x", b & 0xFF));
    return hex.toString();
}

Example of use:

Toast.makeText(this, Utils.getSha256Hash("123456_MY_PASSWORD"), Toast.LENGTH_SHORT).show();
Henrique Monte
  • 1,272
  • 1
  • 15
  • 22
1

i know this has been answered but i found a Hashing Library at android arsenal and its very easy,simple and just one line of code. can hash MD5, SHA-1, SHA-256, SHA-384, or SHA-512.

  1. first add this to your gradle and sync

    implementation 'com.github.1AboveAll:Hasher:1.2'

  2. start hasing

    Hasher.Companion.hash("Hello",HashType.SHA_1);

Ayodele Kayode
  • 304
  • 4
  • 20