0

The Backstory

We've been asked to recreate a legacy system (from Java to PHP, namely Laravel) however it has been stressed that we need to port all the old users to the new system. The old system is using Ofbiz (a very old version too).

The Problem

The old system is using outdated hash algorithms.

I've scoured through the Ofbiz codebase to find the hashing class and located the following functions:

HashCrypt.java

public static String cryptPassword(String hashType, String password) {
    int saltLength = new Random().nextInt(15) + 1;
    return cryptPassword(hashType, RandomStringUtils.random(saltLength, CRYPT_CHAR_SET), password);
}

public static String cryptPassword(String hashType, String salt, String password) {
    StringBuilder sb = new StringBuilder();
    sb.append("$").append(hashType).append("$").append(salt).append("$");
    sb.append(getCrypted(hashType, salt, password));
    return sb.toString();
}

private static String getCrypted(String hashType, String salt, String password) {
    try {
        MessageDigest messagedigest = MessageDigest.getInstance(hashType);
        messagedigest.update(salt.getBytes("UTF-8"));
        messagedigest.update(password.getBytes("UTF-8"));
        return Base64.encodeBase64URLSafeString(messagedigest.digest()).replace('+', '.');
    } catch (NoSuchAlgorithmException e) {
        throw new GeneralRuntimeException("Error while comparing password", e);
    } catch (UnsupportedEncodingException e) {
        throw new GeneralRuntimeException("Error while comparing password", e);
    }
}

I've recreated everything up to:

sb.append(getCrypted(hashType, salt, password));

In PHP without any issues, however, I'm stumped on when getCrypted is called. It runs the following two snippets:

messagedigest.update(salt.getBytes("UTF-8"));
messagedigest.update(password.getBytes("UTF-8"));

I've done some basic debugging and noticed that the salt value is changed when getBytes is performed on it, as an example:

Salt (Pre getBytes): DP1OpoA

Salt (Post getBytes: [B@5338f526

This means that unless I can find a equivalent of getBytes in PHP, it will not be possible for me to recreate this.

The following is the PHP class so far:

OfbizPassword.php

class OfbizPassword {
    const HASH_TYPE = 'sha1';
    const HASH_APPEND = 'SHA';

    public static function hash($password, $salt = '')
    {
        if (empty($salt))
          $salt = self::random_str(random_int(1, 15));

        $hash = '$' . self::HASH_APPEND;
        $hash .=  '$' . $salt;
    }

    private static function random_str($length, $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
    {
        $pieces = [];
        $max = mb_strlen($keyspace, '8bit') - 1;

        for ($i = 0; $i < $length; ++$i) {
            $pieces[] = $keyspace[random_int(0, $max)];
        }

        return implode('', $pieces);
    }

    private static function base64_url_encode($input) {
      return strtr(base64_encode($input), '+/=', '._-');
    }

    private static function base64_url_decode($input) {
      return base64_decode(strtr($input, '._-', '+/='));
    }
}

So, my question is that is there an equivalent to getBytes to get the same output example as given above with the salt?

I am not a Java developer, so please, bear with me.

Script47
  • 14,230
  • 4
  • 45
  • 66
  • 3
    You are getting `[B@5338f526` because you are calling `toString()` on an array, perhaps implicitly. Use `Arrays.toString(thatArray)` if you want to see the string representation of that array. – Andy Turner Jul 18 '18 at 10:46
  • @AndyTurner will try that now to see the output, thanks. – Script47 Jul 18 '18 at 10:46
  • 1
    @AndyTurner excellent, it now prints out the values within the array of bytes, I'm hoping that will get me along further, will report back if any I face any issues. Thanks for the help. – Script47 Jul 18 '18 at 10:51
  • @AndyTurner I now have the bytes as I mentioned above and I am use PHP's [`hash_init`](http://php.net/manual/en/function.hash-init.php), and [`hash_update`](http://php.net/manual/en/function.hash-update.php) to mimic `MessageDigest.update` however, `MessageDigest.update` accepts an array of bytes whereas `hash_update` takes a string, so my question is, what does `MessageDigest.update` do to the array of bytes in the background? – Script47 Jul 18 '18 at 11:22
  • I have looked through the implementations via Eclipse however it made no sense to me as by background is not Java. Does it hash the bytes? Does it convert the bytes back to the characters and hash the characters? Is it all hashed together or is each segment hashed on its own then combined? – Script47 Jul 18 '18 at 11:24

0 Answers0