Search code examples
javaphpencryptionpbkdf2

Using PBKDF2 in Java and PHP


I'm having some difficulty producing the same encrypted password using the PBKDF2 algorithm on both Java and PHP.

I'm using the following Java implementation to generate the hash with a random byte array which is 16 bytes in size. I'm then storing the hash and salt separately in a MySQL database, however when I go to do the same operation in PHP using the salt retrieved from the database, I get almost the exact same encryption except the hash has a leading 0 and I cannot for the life of me figure out why.

Java:

public String hashPassword(String password, byte[] salt){

 char[] passwordChars = password.toCharArray();

     PBEKeySpec spec = new PBEKeySpec(
         passwordChars,
         salt,
         ITERATIONS,
         KEY_LENGTH
     );
     SecretKeyFactory key = null;
    try {
        key = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }

    byte[] hashedPassword = null;

    try {
        hashedPassword = key.generateSecret(spec).getEncoded();
    } catch (InvalidKeySpecException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
     return String.format("%x", new BigInteger(hashedPassword));

}

I found the above code at https://adambard.com/blog/3-wrong-ways-to-store-a-password/

PHP:

$query = $database->query('SELECT * FROM USERS');

$password = 'hello';
$iterations = 1000;

foreach($query as $user){
    $hash = hash_pbkdf2("sha1", $password, $user['salt'], $iterations, 40, false);

}
echo $hash;

Note: There is only one user stored in the database, I know the above code isn't great, I created it quickly for testing purposes.

For both implementations I'm using an iteration count of 1000, a key length of 160 in Java and a key length of 40 in PHP (to compensate for setting raw-output to false)

Java Output - 971f0dddc1bc2e899f2bca178f16ea79bfbbb13
PHP Output - 0971f0dddc1bc2e899f2bca178f16ea79bfbbb13

Any help is much appreciated, thank you.


Solution

  • It is the BigInteger that is killing the leading 0.

    Hashes are not integers, they are an array of 8-bit bytes. Do not try to convert to a BigInteger.

    Either use it as a byte[] or encode as a hexadecimal or Base64 string. To match PHP hexadecimal encode hashedPassword.

    PHP is returning a hexadecimal string encoded hash because raw_output is set to FALSE.