1

我有一个修改后的 MD5 哈希函数,我在 PHP 和 VB.NET 中使用它。当我在本地服务器 (WAMP) 上运行 PHP 代码时,我得到的结果与 VB 版本不同。我尝试在 phpfiddle 上运行脚本,它给出了与 VB 版本相同的结果。

我认为问题可能在于我在 WAMP 服务器上的 PHP 设置?

如果我在运行 WAMP 的 PC 上运行下面的脚本,我得到的结果是:

e5c35f7c3dea80fc68a4031582f34c25

当我在 phpfiddle 或 php 沙箱上运行完全相同的脚本时,我得到的结果是(这是预期的结果):

6337a43e8cd36058e80ae8cb4f465998

4

1 回答 1

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;
}

这个套路的真正细节是从这里无耻地窃取的。还有一个辅助函数来执行左移,因为 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(). 这是因为它使用了右移,这会将符号位的副本从右移。这意味着旋转块的左侧最终将设置所有位,这显然不是我们想要的。我们可以通过创建一个只有右侧集合的目标位的数字来克服这个问题,并将右侧操作数与它进行与运算:

private function rotate ($decimal, $bits)
{
    return dechex(($decimal << $bits) | (($decimal >> (32 - $bits)) & (~(~0 << $bits) & $this->right32)));
}

当你把所有这些放在一起时,你会想出类似这样的东西,它适用于我在 32 位和 64 位系统上。

于 2013-07-01T00:39:46.543 回答