【问题标题】:PHP MD5 Implementation does not produce the correct resultPHP MD5 实现不产生正确的结果
【发布时间】:2017-06-12 09:30:33
【问题描述】:

我一直在尝试在 PHP 中实现 MD5 哈希算法,并生成了以下代码。但是,当我使用测试输入“test”运行该函数时,它会生成字符串“21aa63b9882532cd590623dbd8f2fa225350d682”,而不是预期的“098f6bcd4621d373cade4e832627b4f6”。

我完全不知道为什么这会返回错误,如果有人能帮助我,我将不胜感激。

编辑:我没有在生产中使用它,而是在学校项目中使用它。

我的代码:

<?php

/**
 * Created by PhpStorm.
 * User: Sam Gunner
 * Date: 25/01/2017
 * Time: 18:37
 */
class md5
{
    private $k;
    private $s;

    //Constants as defined by the specification
    private $a0 = 0x67452301;
    private $b0 = 0xefcdab89;
    private $c0 = 0x98badcfe;
    private $d0 = 0x10325476;

    //Convert a character to its binary representation using ASCII
    private function convertCharToByteString($char) {
        $charNum = ord($char);
        $charNumString = decbin($charNum);
        $charNumString = str_pad($charNumString, 8, '0', STR_PAD_LEFT);
        return $charNumString;
    }

    //Takes in a number of zeroes and the string, and then adds that number of zeroes to the end of the string
    private function padZeroRight($str, $amount) {
        for ($i = 0; $i < $amount; $i++) {
            $str .= '0';
        }

        return $str;
    }

    //Splits up a string into several pieces
    private function getCharChunks($original, $length) {
        $chunks = Array();
        $currentChunk = null;
        $currentStart = null;
        $numberOfParts = ceil(strlen($original) / $length); //Get the number of chunks

        for ($i = 0; $i < $numberOfParts; $i++) {
            $currentStart = ($i * $length) - ($length - 1); //Get the starting position of the substring
            $currentChunk = substr($original, $currentStart, $length);
            $chunks[$i] = $currentChunk;
        }

        return $chunks;
    }

    //Easy way of converting multiple binary integers to array of decimal integers
    private function convertChunkArrayToIntegers($chunkArray) {
        $finalChunks = Array();
        for ($i = 0; $i < count($chunkArray); $i++) {
            $finalChunks[$i] = decbin($chunkArray[$i]);
        }

        return $finalChunks;
    }

    //Begin MD5-specific functions
    private function F($B, $C, $D) {
        return ($B & $C) | ((~$B) & $D);
    }

    private function G($B, $C, $D) {
        return ($B & $D) | ($C & (~$D));
    }

    private function H($B, $C, $D) {
        return ($B ^ $C ^ $D);
    }

    private function I($B, $C, $D) {
        return ($C ^ ($B | (~$D)));
    }

    private function rotate($decimal, $bits) { //returns hex
        return (($decimal << $bits) |  ($decimal >> (32 - $bits))) & 0xffffffff;
    }

    public function __construct() {
        $this->s = Array(
            7, 12, 17, 22,  7, 12, 17, 22,  7, 12, 17, 22,  7, 12, 17, 22,
            5,  9, 14, 20,  5,  9, 14, 20,  5,  9, 14, 20,  5,  9, 14, 20,
            4, 11, 16, 23,  4, 11, 16, 23,  4, 11, 16, 23,  4, 11, 16, 23,
            6, 10, 15, 21,  6, 10, 15, 21,  6, 10, 15, 21,  6, 10, 15, 21
        );

        for ($i = 0; $i < 64; $i++) { //Generate the constants that are defined in the specification
            $this->k[$i] = floor(abs(sin($i + 1)) * pow(2, 32));
        }
    }

    //Returns the MD5 hash of a message in string form
    public function hash($message) {
        $finalStr = '';
        $subChunks = null;
        $A = null;
        $B = null;
        $C = null;
        $D = null;
        $F = null;
        $g = null;
        $DTemp = null;

        for ($i = 0; $i < strlen($message); $i++) { //Change the string representation of the message into a series of bits, which we can manipulate
            $finalStr .= $this->convertCharToByteString(substr($finalStr, $i, 1));
        }

        $finalStr .= '1'; //Append 1, as the specification says to

        $messageLen = strlen($finalStr);
        $messageLenFinal = $messageLen % 512; //Find out how much we are under a multiple of 512

        if ($messageLenFinal > 448) { //If the message length remainder is longer than 448, then we need to add some more zeroes to get it to 448 over
            $remainingToAdd = (512 - $messageLenFinal) + 448; //'tip it over the edge' and then add 448 to make it 64 below 512
        } else {
            $remainingToAdd = 448 - $messageLenFinal;
        }

        $finalStr = $this->padZeroRight($finalStr, $remainingToAdd); //Add zeroes onto the end until criteria is met
        //$messageLen = $messageLen % (pow(2, 64)); //Get the length of the message MOD 2 pow 64
        $messageLen = strlen($finalStr);

        $messageLenStr = decbin($messageLen); //Convert the decimal representation to binary
        $messageLenStr = str_pad($messageLenStr, 64, "0", STR_PAD_LEFT); //Pad the message with zeroes to make it 64 bits long

        $finalStr .= $messageLenStr;

        $chunks = $this->getCharChunks($finalStr, 512); //Get message in 512-bit chunks

        foreach ($chunks as $chunk) {
            $subChunks = $this->convertChunkArrayToIntegers($this->getCharChunks($chunk, 32)); //Get sub chunks of 32-bit size
            $A = $this->a0;
            $B = $this->b0;
            $C = $this->c0;
            $D = $this->d0;

            for ($i = 0; $i < 64; $i++) {
                if ($i >= 0 && $i < 16) {
                    $F = $this->F($B, $C, $D);
                    $g = $i;
                } elseif ($i > 15 && $i <32) {
                    $F = $this->G($B, $C, $D);
                    $g = ((5 * $i) + 1) % 16;
                } elseif ($i > 31 && $i < 48) {
                    $F = $this->H($B, $C, $D);
                    $g = ((3 * $i) + 5) % 16;
                } elseif ($i > 47 && $i < 64) {
                    $F = $this->I($B, $C, $D);
                    $g = (7 * $i) % 16;
                }

                $DTemp = $D;
                $D = $C;
                $C = $B;
                $B = $B + $this->rotate(($A + $F + $this->k[$i] + $subChunks[$g]), $this->s[$i]);
                $A = $DTemp;
            }

            $this->a0 += $A;
            $this->b0 += $B;
            $this->c0 += $C;
            $this->d0 += $D;
        }

        $final = dechex($this->a0) . dechex($this->b0) . dechex($this->c0) . dechex($this->d0);
        return $final;
    }
}

非常感谢,

- 山姆

【问题讨论】:

  • 为什么要使用 MD5 作为密码?这不应该用于现场制作;不是在这个时代。
  • 啊,我应该说我这样做是为了一个学校项目,而不是实际在生产中使用它。我知道算法的错误

标签: php oop hash passwords md5


【解决方案1】:

你在一轮结束时执行的四个加法:

$this->a0 += $A;
$this->b0 += $B;
$this->c0 += $C;
$this->d0 += $D;

没有换行。这会导致 A/B/C/D 变得太大——这可能就是为什么你的输出最终是 160 位而不是 128 位的原因。

请注意,您还需要在最后阶段填充 dechex() 的结果,以避免在四个组件之一最终非常小的情况下给出太短的结果。

(可能还有其他问题——这些只是我注意到的第一个问题。)


如果它是一个选项,我会强烈建议您在这个项目中使用 C。 PHP 不太适合编写低级加密代码;您会发现自己经常与此类问题作斗争。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-12-14
    • 2015-10-12
    • 2015-01-06
    • 1970-01-01
    • 1970-01-01
    • 2017-04-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多