我有一个修改过的MD5哈希函数,我在PHP和VB.NET中使用它。当我在本地服务器(WAMP)上运行PHP代码时,我得到了与VB版本不同的结果。我试过在phpfiddle上运行脚本,它提供与VB版本相同的结果。
我认为问题可能在于我在WAMP服务器上的PHP设置?
如果我在运行WAMP的PC上运行下面的脚本,我得到的结果是:
e5c35f7c3dea80fc68a4031582f34c25
当我在phpfiddle或php沙箱上运行完全相同的脚本时,得到的结果是(这是预期结果):
6337a43e8cd36058e80ae8cb4f465998
答案 0 :(得分:2)
暂时搁置一下,你在这里所做的事情听起来像是一个糟糕的方法,你想要解决的实际问题是什么,这里是对这个问题的直接回答。
正如我在上面的评论中已经概述的那样,你遇到的问题的根本原因是PHP没有无符号整数的概念,它通过将溢出整数边界的数字转换为浮点数来处理这个问题。按位运算不好用)。这意味着,在32位系统上,您的代码将无法正常工作,因为MD5使用无符号32位整数。
您需要确保您的代码是“二进制安全的” - 这样所有数字都表示为无符号32位整数。
为此,您需要重新实现加法运算符,并(使用当前实现)bindec()
/ hexdec()
函数。值得注意的是,您当前对某些过程的处理效率非常低 - 所有转换为十六进制字符串或从十六进制字符串转换的地方,以及二进制表示为ASCII字符串的地方 - 但我现在将向您展示如何快速 - 修复你当前的实现。
首先让我们来看看加法操作:
private function binarySafeAddition($a, $b)
{
// NB: we don't actually need 64 bits, theoretically we only need 33
// but 40 bit integers are confusing enough, and 33 bits is unrepresentable
$a = "\x00\x00\x00\x00" . pack('N', $a);
$b = "\x00\x00\x00\x00" . pack('N', $b);
$carry = $a & $b;
$result = $a ^ $b;
while ($carry != "\x00\x00\x00\x00\x00\x00\x00\x00") {
$shiftedcarry = $this->leftShiftByOne($carry);
$carry = $result & $shiftedcarry;
$result ^= $shiftedcarry;
}
return current(unpack('N', substr($result, 4)));
}
private function leftShiftByOne($intAsStr)
{
$p = unpack('N2', $intAsStr);
return pack('N2', ($p[1] << 1) | (($p[2] >> 31) & 0x00000001), $p[2] << 1);
}
private function add()
{
$result = 0;
foreach (func_get_args() as $i => $int) {
$result = $this->binarySafeAddition($result, $int);
}
return $result;
}
从here无耻地偷走了这个例行公事的真正具体细节。还有一个辅助函数来执行左移,因为PHP不允许你左移字符串和一个方便的包装函数,允许我们在一次干净的调用中一起添加任意数量的操作数。
接下来让我们看一下bindec()
和hexdec()
替代品:
private function binarySafeBinDec($bin)
{
$bits = array_reverse(str_split($bin, 1));
$result = 0;
foreach ($bits as $position => $bit) {
$result |= ((int) $bit) << $position;
}
return $result;
}
private function binarySafeHexDec($hex)
{
$h = str_split(substr(str_pad($hex, 8, '0', STR_PAD_LEFT), -8), 2);
return (hexdec($h[0]) << 24) | (hexdec($h[1]) << 16) | (hexdec($h[2]) << 8) | hexdec($h[3]);
}
希望这些是合理的自我解释,但随时可以询问任何你不理解的事情。
我们还需要用二进制安全实现替换所有这些0xffffffff
十六进制文字,因为这些也会导致32位系统上的浮点数。这是一种安全的方法,可以将最右边的32位设置为整数,这将适用于32位和64位系统:
private $right32;
public function __construct()
{
$this->right32 = ~((~0 << 16) << 16);
}
还有一种我们需要重新实现的方法,那就是rotate()
。这是因为它使用右移,这会从右侧移开符号位的副本。这意味着旋转块的左侧将以其设置的所有位结束,这显然不是我们想要的。我们可以通过创建一个只包含右侧设置的目标位的数字,并用右边的操作数与它进行AND运算来克服这个问题:
private function rotate ($decimal, $bits)
{
return dechex(($decimal << $bits) | (($decimal >> (32 - $bits)) & (~(~0 << $bits) & $this->right32)));
}
当你把所有这些放在一起时,你想出了类似this的东西,它适用于32位和64位系统。