【问题标题】:Can you convert the output of php crypt() to valid MD5?您可以将 php crypt() 的输出转换为有效的 MD5 吗?
【发布时间】:2010-10-02 11:39:48
【问题描述】:

我有一些使用PHP function crypt() 加密的字符串。

输出如下所示:

$1$Vf/.4.1.$CgCo33ebiHVuFhpwS.kMI0
$1$84..vD4.$Ps1PdaLWRoaiWDKCfjLyV1
$1$or1.RY4.$v3xo04v1yfB7JxDj1sC/J/

虽然我相信 crypt() 使用的是 MD5 算法,但输出不是有效的 MD5 哈希。

有没有办法将生成的哈希转换为有效的 MD5 哈希(16 字节十六进制值)?


更新:

感谢您的回复,所以到目前为止的答案。我很确定使用的 crypt 函数正在使用某种 MD5 算法。我要做的是将我拥有的输出转换为类似于以下内容的 MD5 哈希:

9e107d9d372bb6826bd81d3542a419d6  
e4d909c290d0fb1ca068ffaddf22cbd0  
d41d8cd98f00b204e9800998ecf8427e

(取自Wikipedia

有没有一种方法可以将我拥有的哈希值转换为上述哈希值?

【问题讨论】:

  • 您能详细说明一下您要完成的工作吗?没有冒犯的意思,但这听起来像是如何对密码数据库进行彩虹表攻击的秘诀中的第 1 步,除非确信这不是出于邪恶目的,否则人们可能不愿意提供帮助……
  • 一个结果是base64编码,另一个只是base16编码。
  • @genesis:你为什么要更改链接到的 URL,而不是页面上显示的 URL?
  • @PaloEbermann:因为我忘记了那个

标签: php hash cryptography md5 crypt


【解决方案1】:

好的,所以这个答案可能晚了一年,但我会试一试。在您自己的回答中,您注意到crypt() 使用的是 FreeBSD MD5,它还在运行哈希之前对 salt 进行了一些有趣的转换,因此我将要给您的结果永远不会与调用md5() 的结果。也就是说,您看到的输出与您习惯的格式之间的唯一区别是您看到的输出编码如下

$1$        # this indicates that it is MD5
Vf/.4.1.   # these eight characters are the significant portion of the salt
$          # this character is technically part of the salt, but it is ignored
CgCo33eb   # the last 22 characters are the actual hash
iHVuFhpw   # they are base64 encoded (to be printable) using crypt's alphabet
S.kMI0     # floor(22 * 6 / 8) = 16 (the length in bytes of a raw MD5 hash)

据我所知,crypt 使用的字母如下所示:

./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz

因此,考虑到所有这些,以下是如何将 22 个字符的 crypt-base64 哈希转换为 32 个字符的 base16(十六进制)哈希:

首先,您需要将 base64(带有自定义字母)转换为原始 16 字节 MD5 哈希。

define('CRYPT_ALPHA','./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz');
/**
 * Decodes a base64 string based on the alphabet set in constant CRYPT_ALPHA
 * Uses string functions rather than binary transformations, because said
 * transformations aren't really much faster in PHP
 * @params string $str  The string to decode
 * @return string       The raw output, which may include unprintable characters
 */
function base64_decode_ex($str) {
    // set up the array to feed numerical data using characters as keys
    $alpha = array_flip(str_split(CRYPT_ALPHA));
    // split the input into single-character (6 bit) chunks
    $bitArray = str_split($str);
    $decodedStr = '';
    foreach ($bitArray as &$bits) {
        if ($bits == '$') { // $ indicates the end of the string, to stop processing here
            break;
        }
        if (!isset($alpha[$bits])) { // if we encounter a character not in the alphabet
            return false;            // then break execution, the string is invalid
        }
        // decbin will only return significant digits, so use sprintf to pad to 6 bits
        $decodedStr .= sprintf('%06s', decbin($alpha[$bits]));
    }
    // there can be up to 6 unused bits at the end of a string, so discard them
    $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);
}

既然您已经获得了原始数据,您需要一种方法将其转换为您期望的 base-16 格式,这再简单不过了。

/**
 * Takes an input in base 256 and encodes it to base 16 using the Hex alphabet
 * This function will not be commented.  For more info:
 * @see http://php.net/str-split
 * @see http://php.net/sprintf
 *
 * @param string $str   The value to convert
 * @return string       The base 16 rendering
 */
function base16_encode($str) {
    $byteArray = str_split($str);
    foreach ($byteArray as &$byte) {
        $byte = sprintf('%02x', ord($byte));
    }
    return join($byteArray);
}

最后,由于 crypt 的输出包含了很多我们在这个过程中不需要(事实上也不能使用)的数据,所以一个简短而实用的函数不仅可以将这两者联系在一起,而且还允许直接crypt 输出的输入。

/**
 * Takes a 22 byte crypt-base-64 hash and converts it to base 16
 * If the input is longer than 22 chars (e.g., the entire output of crypt()),
 * then this function will strip all but the last 22.  Fails if under 22 chars
 *
 * @param string $hash  The hash to convert
 * @param string        The equivalent base16 hash (therefore a number)
 */
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));
}

鉴于这些函数,您的三个示例的 base16 表示是:

3ac3b4145aa7b9387a46dd7c780c1850
6f80dba665e27749ae88f58eaef5fe84
ec5f74086ec3fab34957d3ef0f838154

当然,重要的是要记住它们始终有效,只是格式不同。

【讨论】:

  • 恐怕 md5-crypt 的输出与 md5 散列的区别不仅仅在于使用自定义字母表的 base64 编码。查看其中一个实现实例,google.com/codesearch/p?hl=en#eiS4vny31P0/Linux-PAM-0.99.7.0/… md5-crypt 还以使加密计算密集的方式按摩哈希(相对于当时可用的计算能力,请参阅关于 60MHz Pentium 的评论) .您提供的解决方案不会反转该转换。
  • 天哪,从坟墓里回来给我投了反对票,而且还不是基于这个问题?手头的问题是来自crypt() 函数的值看起来不像提问者想要的那样——这是解决提问者关于哈希的视觉表示的问题,而不是使哈希产生md5() 等于 crypt() 产生的哈希值。在我看来,这不是否决按钮的用途。
  • base64 编码版本没有填充。散列长度为 128 位,因此在一次遍历 6 位之后,您将剩下 2 位。一个明显的解决方案是将这些用作 LSB 并照常使用索引。奇怪的是,虽然每个提供的哈希都以 00 位对结尾,但它们的 base64 表示的最后一个字符(来自 Q)不同。你能解释一下吗?
  • @buherator,(1/2) 抱歉,我没有早点注意到这一点。首先,22 个字符 x 6 位 = 132 位数据。我们可以从 132 Mod 8 中取出额外的 4 位并创建另一个字节,但这不太可能是正确的解决方案,因为 (a) 这意味着我们只能为非常特定范围内的最后一个字节生成输出(高或低阶),这似乎不太可能,并且(b)已知 MD5 散列的长度为 128 位。这似乎表明剩余的 4 位可以被丢弃,但沿着这条路走下去,我在下一条评论中找到了另一条路。
  • @buherator,(2/2)。在分析了样本哈希后,我注意到最后四位总是有一些数据被我截断了;事实上,最后一个字符似乎总是一个相对较低的数字,只有最后两位有数据。这很可能意味着两件事之一(可能两者兼而有之): (a) 我的自定义 B64 字母表是错误的。 (b) 值以 little-endian 顺序序列化。测试使第二种情况看起来更有可能,并在base64_decode_ex 中使用sprintf 周围的strrev 修复它。
【解决方案2】:

$1$ 确实意味着这是一个 MD5 哈希,但 crypt 生成一个随机盐。这就是您找到不同 MD5 值的原因。如果包含生成的盐,您会发现相同的结果。

盐在输出中被base64编码,作为散列。

使用的算法是系统范围的参数。通常这是 MD5,你是对的。

【讨论】:

    【解决方案3】:

    我相信我最初问题的答案是否定的,您不能从一种格式转换为另一种格式。

    php crypt() 生成的哈希似乎是由 Poul-Henning Kamp 创建的 FreeBSD MD5 哈希实现的一个版本生成的。

    http://people.freebsd.org/~phk/

    【讨论】:

      【解决方案4】:

      根据文档,这取决于系统。您可以通过设置 salt 参数来强制使用算法。来自文档:

      加密类型由以下触发 盐的论点。在安装时, PHP 决定了 crypt 函数并接受盐 对于其他加密类型。如果没有盐 提供,PHP 将自动生成一个 标准的两个字符盐 默认,除非默认加密 系统上的类型是 MD5,其中 情况下随机的 MD5 兼容盐是 生成。

      【讨论】:

        【解决方案5】:

        来自http://php.net/crypt

        crypt() 将使用基于标准 Unix DES 的加密算法或系统上可能可用的替代算法返回加密字符串。

        你想要md5()函数:

        使用 » RSA Data Security, Inc. MD5 消息摘要算法计算 str 的 MD5 哈希,并返回该哈希。
        如果可选的 raw_output 设置为 TRUE,则 md5 摘要以长度为 16 的原始二进制格式返回。 默认为假。

        【讨论】:

        • 实际上,其中一种替代算法($1$ 表示的这一算法)是一种(盐渍的)MD5 算法。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-07-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-08-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多