2

How does digest.update(salt) in the getHash(...) method work in the code example below? I need to duplicate the logic in PHP. The testHash() method has some embedded test values. I can get a match if I comment out digest.update(salt). I've tried the following combination in PHP to get a match with no luck (pseudocode):

  • sha256($salt.$pass)
  • sha256($pass.$salt)
  • sha256($pass.sha256($salt))
  • sha256($salt.sha256($pass))
  • sha256(sha256($pass).sha256($salt))
  • sha256(sha256($salt).sha256($pass))

Code:

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class Owasp {

    private final static int ITERATION_NUMBER = 5;

    public void testhash() throws IOException, NoSuchAlgorithmException, UnsupportedEncodingException {
        byte[] bSalt = base64ToByte("123456");
        byte[] bDigest = getHash(ITERATION_NUMBER, "somepass", bSalt);
        String sDigest = byteToBase64(bDigest);
        System.out.println(sDigest);
    }

    public byte[] getHash(int iterationNb, String password, byte[] salt) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        digest.reset();
        digest.update(salt);
        byte[] input = digest.digest(password.getBytes("UTF-8"));
        for (int i = 0; i < iterationNb; i++) {
            digest.reset();
            input = digest.digest(input);
        }
        return input;
    }

    public static byte[] base64ToByte(String data) throws IOException {
        BASE64Decoder decoder = new BASE64Decoder();
        return decoder.decodeBuffer(data);
    }

    public static String byteToBase64(byte[] data) {
        BASE64Encoder endecoder = new BASE64Encoder();
        return endecoder.encode(data);
    }

    public static void main(String[] args) throws NoSuchAlgorithmException, UnsupportedEncodingException, IOException {
        Owasp t = new Owasp();
        t.testhash();
    }
}

EDIT

I've added the PHP code I've been using. It's not ideal in that I don't enumerate all the cases I've tested. Stepping through the debugger in Netbeans with an rt.jar file that I compiled with -g it looks like it's being stored as $salt.$pass. Using the PHP code I am not able to get a match.

PHP Code:

<?php
//Without salt
$pass = "somepass";

$hash = hash('sha256', $pass, true);
for($i = 0; $i < 5; $i++) {
    $hash = hash('sha256', $hash, true);
}

echo "Java output without salt: " . "r+G0EnbjURjk99+wMvjnJV51d/tVX8tAn+7VAUCPXRY=" . "\n";
echo "PHP output without salt : " . base64_encode($hash) . "\n";


//With salt
$pass = "somepass";
$salt = "123456";

//I've tried all sorts of combinations and manipulation.
$hash = hash('sha256', $salt.$pass, true);
for($i = 0; $i < 5; $i++) {
    $hash = hash('sha256', $hash, true);
}

echo "Java output with salt: " . "vxZP4vmc+dixEhsZW58+zViLn7XXymirCP7kbl2KtT0=" . "\n";
echo "PHP output with salt : " . base64_encode($hash) . "\n";

UPDATE

I've been glued to the debugger in Netbeans for hours now trying to figure out what's happening when digest.update($salt) is called. MessageDigestSPI is the key. engineUpdate(byte[] input, int offset, int len) is called but I can't find the implementation. All the engineUpdate methods might be pointing to engineUpdate(ByteBuffer input) but I can't confirm. I've done Java work before, but it was years ago. I've tried every I've hex2bin'ed, packed, unpacked, and Base64'ed every which way with no luck :( I'll keep you all posted.

1 Answers1

0

The algorithm is basically hash(hash(hash(hash(...hash(password)...))))), as many times as dictated by the iterationNb parameter.

The proper way to use the salt is to concatenate it with the password, and then hashing it. This is done in your code by doing a digest.update(salt), and then calling digest with the password. It's essentially doing the same as the following:

  String saltedPassword = password + new String(salt, "UTF-8");
  byte[] input = digest.digest(saltedPassword.getBytes("UTF-8"));

Of course, like any proper salt, you need to store it along with the hashed password, to be able to verify it in the future.

You can get more information on how salt works for storing password hashes at this Wikipedia link. Also, some best practices for using salt when storing passwords at this other StackOverflow question.

Community
  • 1
  • 1
mjuarez
  • 16,372
  • 11
  • 56
  • 73