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.