OK, so maybe this answer is a year late, but I'll give it a shot. In your own answer, you note that crypt()
is using the FreeBSD MD5, which also does some interesting transformations on the salt before running the hash, so the result of what I'm about to give you will never quite match up with the results of a call to md5()
. That said, the only difference between the output you are seeing and the format you are used to is that the output you are seeing is encoded as follows
$1$
Vf/.4.1.
$
CgCo33eb
iHVuFhpw
S.kMI0
To my knowledge, the alphabet used by crypt looks like this:
./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
So, with all of this borne in mind, here is how you can convert the 22 character crypt-base64 hash into a 32 character base16 (hexadecimal) hash:
First, you need something to convert the base64 (with custom alphabet) into a raw 16-byte MD5 hash.
define('CRYPT_ALPHA','./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz');
function base64_decode_ex($str) {
$alpha = array_flip(str_split(CRYPT_ALPHA));
$bitArray = str_split($str);
$decodedStr = '';
foreach ($bitArray as &$bits) {
if ($bits == '$') {
break;
}
if (!isset($alpha[$bits])) {
return false;
}
$decodedStr .= sprintf('%06s', decbin($alpha[$bits]));
}
$decodedStr = substr($decodedStr, 0, strlen($decodedStr) - (strlen($decodedStr) % 8));
$byteArray = str_split($decodedStr, 8);
foreach ($byteArray as &$byte) {
$byte = chr(bindec($byte));
}
return join($byteArray);
}
Now that you've got the raw data, you'll need a method to convert it to the base-16 format you're expecting, which couldn't be easier.
function base16_encode($str) {
$byteArray = str_split($str);
foreach ($byteArray as &$byte) {
$byte = sprintf('%02x', ord($byte));
}
return join($byteArray);
}
Finally, since the output of crypt includes a lot of data we don't need (and, in fact, cannot use) for this process, a short and sweet function to not only tie these two together but to allow for direct input of output from crypt.
function md5_b64tob16($hash) {
if (strlen($hash) < 22) {
return false;
}
if (strlen($hash) > 22) {
$hash = substr($hash,-22);
}
return base16_encode(base64_decode_ex($hash));
}
Given these functions, the base16 representation of your three examples are:
3ac3b4145aa7b9387a46dd7c780c1850
6f80dba665e27749ae88f58eaef5fe84
ec5f74086ec3fab34957d3ef0f838154
Of course, it is important to remember that they were always valid, just formatted differently.