3

Can someone figure out why the output of these (php and java) snippets of code don't return the same SHA512 for the same input?

$password = 'whateverpassword';
$salt = 'ieerskzcjy20ec8wkgsk4cc8kuwgs8g';
$salted = $password.'{'.$salt.'}';
$digest = hash('sha512', $salted, true);
echo "digest: ".base64_encode($digest);
for ($i = 1; $i < 5000; $i++) {
  $digest = hash('sha512', $digest.$salted, true);
}
$encoded_pass = base64_encode($digest);
echo $encoded_pass;

This is the code on the android application:

public String processSHA512(String pw, String salt, int rounds)
{
    try {
        md = MessageDigest.getInstance("SHA-512");
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
        throw new RuntimeException("No Such Algorithm");
    }

    String result = hashPw(pw, salt, rounds);
    System.out.println(result);
    return result;
}

private static String hashPw(String pw, String salt, int rounds) {
    byte[] bSalt;
    byte[] bPw;

    String appendedSalt = new StringBuilder().append('{').append(salt).append('}').toString();

    try {
        bSalt = appendedSalt.getBytes("ISO-8859-1");
        bPw = pw.getBytes("ISO-8859-1");
    } catch (UnsupportedEncodingException e) {
        throw new RuntimeException("Unsupported Encoding", e);
    }

    byte[] digest = run(bPw, bSalt);
    Log.d(LCAT, "first hash: " + Base64.encodeBytes(digest));
    for (int i = 1; i < rounds; i++) {
        digest = run(digest, bSalt);
    }

    return Base64.encodeBytes(digest);
}

private static byte[] run(byte[] input, byte[] salt) {
    md.update(input);
    return md.digest(salt);
}

The library for base64 encoding is this: base64lib

This java code is actually some modified code I found around another question in StackOverflow. Although the Android code is running fine it doesn't match with the output from the php script. It doesn't even match the first hash!

Note 1: On php hash('sha512',$input, $raw_output) returns raw binary output

Note 2: On java I tried to change the charset (UTF-8, ASCII) but it also didn't work.

Note 3: The code from the server can not be changed, so I would appreciate any answer regarding how to change my android code.

Elnur Abdurrakhimov
  • 44,533
  • 10
  • 148
  • 133
Joscandreu
  • 73
  • 2
  • 9
  • For anyone having problem with sha512 in Android and PHP, take a look at this: http://www.coderblog.de/producing-the-same-sha-512-hash-in-java-and-php/#comment-614 ... It helped me :) – Buksy Sep 19 '13 at 19:25

2 Answers2

2

The first hash should be the same on the server and in Java. But then in the loop what gets appended to the digest is password{salt} in the PHP code, but only {salt} in the Java code.

Henry
  • 42,982
  • 7
  • 68
  • 84
  • Yes, you're right. I changed the code with your advice but it didn't generate the same output. However, the first hash should be the same. – Joscandreu Feb 08 '13 at 13:38
  • @Joscandreu The first hash is already different or is it the same? – Henry Feb 08 '13 at 14:29
  • The first hash is different. My guess is that is has to do something with the binary interpretation of the strings but changing the charset doesn't affect the java output. I forced the ISO-8859-1 in java because is what appears to be the default charset for php in my installation. – Joscandreu Feb 08 '13 at 14:41
  • @Joscandreu Yes it could be an encoding issue (I don't see another reason). But according to this question http://stackoverflow.com/questions/4680661/java-sha256-outputs-different-hash-to-php-sha256 it seems to work for ISO-8859-1 or UTF-8 (for ASCII input the two encodings agree). – Henry Feb 08 '13 at 15:03
  • After reading this: http://www.daniweb.com/web-development/php/threads/319067/php-vb.net-encryption-problem-md5# I am suspicious about the encodebase64 function, where there problem might be. The encode function in php accepts a string as input an it actually is raw binary. So it could be that php is managing some silent internal conversion. I'll dig into this if I can. – Joscandreu Feb 08 '13 at 16:20
  • Thanks for your responses. Actually it was my fault. The code with your rectification works perfectly. I was not passing the appropiate parameters. I am marking your answer accepted. – Joscandreu Feb 11 '13 at 15:49
  • @Joscandreu this was an almost trivial problem in OP's code. I guess you can easily fix it. – Henry Sep 25 '19 at 13:22
0

For the lazy ones, one example better than a thousand words ;). I finally understood what was happening. The method update appends bytes to the digest, so when you append $password.{$salt} is the same as doing mda.update(password bytes) and the mda.digest("{$salt}" bytes. I do that answer because I was going crazy finding why it was not working and it was all in this answer.

Thanks guys. This is the example that works in a Java Server:

public static String hashPassword(String password, String salt) throws Exception {
        String result = password;
        String appendedSalt = new StringBuilder().append('{').append(salt).append('}').toString();
        String appendedSalt2 = new StringBuilder().append(password).append('{').append(salt).append('}').toString();

        if(password != null) {
            //Security.addProvider(new BouncyCastleProvider());

            MessageDigest mda = MessageDigest.getInstance("SHA-512");
            byte[] pwdBytes = password.getBytes("UTF-8");
            byte[] saltBytes = appendedSalt.getBytes("UTF-8");
            byte[] saltBytes2 = appendedSalt2.getBytes("UTF-8");
            byte[] digesta = encode(mda, pwdBytes, saltBytes);

            //result = new String(digesta);
           System.out.println("first hash: " + new String(Base64.encode(digesta),"UTF-8"));
                for (int i = 1; i < ROUNDS; i++) {

                    digesta = encode(mda, digesta, saltBytes2);
                }
                System.out.println("last hash: " + new String(Base64.encode(digesta),"UTF-8"));

                result = new String(Base64.encode(digesta));
        }
        return result;
    }

private static byte[] encode(MessageDigest mda, byte[] pwdBytes,
            byte[] saltBytes) {
        mda.update(pwdBytes);
        byte [] digesta = mda.digest(saltBytes);
        return digesta;
    }
Kasas
  • 1,216
  • 2
  • 18
  • 28