无法解密

时间:2016-03-08 04:02:38

标签: php encoding

为了能够创建一个6个字符不区分大小写的可逆灰烬,我使用了我在这里找到的代码:http://web.archive.org/web/20130727034425/http://blog.kevburnsjr.com/php-unique-hash,它与base64一起使用并将其转换为与base36一起使用。

但是,我无法将哈希值反转为原始值,因为带有base64的类的原始代码确实如此。

修改 为了回应我在这里得到的反馈,我知道我没有使用正确的术语。我知道哈希是什么意思,加密是什么,但我只是使用它,因为它是我用于此目的的原始代码中呈现的方式。 我没有花时间更好地解释我真正想要的东西,但是我的目标是将任何整数从1转换为10,000或8字节的唯一字符串。

编辑:在下面发布我的解决方案作为答案。

class PseudoCrypt {

    /* Key: Next prime greater than 36 ^ n / 1.618033988749894848 */
    /* Value: modular multiplicative inverse */
    private static $golden_primes = array(
        '1'                  => '1',
        '41'                 => '59',
        '2377'               => '1677',
        '147299'             => '187507',
        '9132313'            => '5952585',
        '566201239'          => '643566407',
        '35104476161'        => '22071637057',
        '2176477521929'      => '294289236153',
        '134941606358731'    => '88879354792675',
        '8366379594239857'   => '7275288500431249',
        '518715534842869223' => '280042546585394647'
    );

    /* Ascii :                    0  9,         A  Z,         a  z     */
    /* $chars = array_merge(range(48,57), range(65,90), range(97,122)) */
    private static $chars36 = array(
        0=>48,
        1=>49,
        2=>50,
        3=>51,
        4=>52,
        5=>53,
        6=>54,
        7=>55,
        8=>56,
        9=>57,
        10=>65,
        11=>66,
        12=>67,
        13=>68,
        14=>69,
        15=>70,
        16=>71,
        17=>72,
        18=>73,
        19=>74,
        20=>75,
        21=>76,
        22=>77,
        23=>78,
        24=>79,
        25=>80,
        26=>81,
        27=>82,
        28=>83,
        29=>84,
        30=>85,
        31=>86,
        32=>87,
        33=>88,
        34=>89,
        35=>90
    );


    public static function base36($int) {
        $key = '';
        while($int > 0) {
        $mod = $int-(floor($int/36)*36);
        $key .= chr(self::$chars36[$mod]);
        $int = floor($int/36);
        }
        return strrev($key);
    }

    public static function hash($num, $len = 5) {
        $ceil = bcpow(36, $len);
        $primes = array_keys(self::$golden_primes);
        $prime = $primes[$len];
        $dec = bcmod(bcmul($num, $prime), $ceil);
        $hash = self::base36($dec);
        return str_pad($hash, $len, "0", STR_PAD_LEFT);
    }

    public static function unbase36($key) {
        $int = 0;
        foreach(str_split(strrev($key)) as $i => $char) {
            $dec = array_search(ord($char), self::$chars36);
            $int = bcadd(bcmul($dec, bcpow(36, $i)), $int);
        }
        return $int;
    }

    public static function unhash36($num, $len = 5) {
        $ceil = pow(36, $len);
        $prime = self::$golden_primes[$len];
        $dec = ($num * $prime)-floor($num * $prime/$ceil)*$ceil;
        $hash = self::base36($dec);
        return str_pad($hash, $len, “0″, STR_PAD_LEFT);
    }

}

echo "<pre>";

foreach(range(1, 100000) as $n) {
    echo $n." - ";
    $hash = PseudoCrypt::hash($n, 8);
    echo $hash." - ";
    echo PseudoCrypt::unhash36($hash)."<br/>";
}

4 个答案:

答案 0 :(得分:1)

我自己开发了解决方案,所以我在这里分享代码。

我的问题中代码的问题是$ golden_primes数组需要具有不同的值以匹配基于36个字符而不是62的素数。

它完美地生成独特的字符串(大写字母和数字),使用1到99,999,999,999,999(最多9个字符可逆&#34;哈希&#34;)的数字进行测试,并且没有发生任何冲突。

代码:

<?php

class PseudoCrypt {

    /* Key: Next prime greater than 36 ^ n / 1.618033988749894848 */
    /* Value: modular multiplicative inverse */
    private static $golden_primes = array(
        '1' =>'1',
        '23' =>'11',
        '809' =>'809',
        '28837' => '29485',
        '1038073' =>'179017', 
        '37370153' => '47534873' ,
        '1345325473' => '264202849', 
        '48431716939' => '19727015779',
        '1743541808839' =>'1532265214711',
        '62767505117101' =>'67935388019749'
    );

    /* Ascii :                    0  9,         A  Z,         a  z     */
    /* $chars = array_merge(range(48,57), range(65,90), range(97,122)) */
    private static $chars36 = array(0=>48, 1=>49, 2=>50, 3=>51, 4=>52, 5=>53, 6=>54, 7=>55, 8=>56, 9=>57,10=>65,11=>66,12=>67,13=>68,14=>69,15=>70,16=>71,17=>72,18=>73,19=>74,20=>75,21=>76,22=>77,23=>78,24=>79,25=>80,26=>81,27=>82,28=>83,29=>84,30=>85,31=>86,32=>87,33=>88,34=>89,35=>90);

    public static function base36($int) {
        $key = "";
        while($int > 0) {
        $mod = $int-(floor($int/36)*36);
        $key .= chr(self::$chars36[$mod]);
        $int = floor($int/36);
        }
        return strrev($key);
    }

    public static function unbase36($key) {
        $int = 0;
        foreach(str_split(strrev($key)) as $i => $char) {
            $dec = array_search(ord($char), self::$chars36);
            $int = bcadd(bcmul($dec, bcpow(36, $i)), $int);
        }
        return $int;
    }

    public static function hash($num, $len = 5) {
        $ceil = bcpow(36, $len);
        $primes = array_keys(self::$golden_primes);
        $prime = $primes[$len];
        $dec = bcmod(bcmul($num, $prime), $ceil);
        $hash = self::base36($dec);
        return str_pad($hash, $len, "0", STR_PAD_LEFT);
    }

    public static function unhash($hash) {
        $len = strlen($hash);
        $ceil = bcpow(36, $len);
        $mmiprimes = array_values(self::$golden_primes);
        $mmi = $mmiprimes[$len];
        $num = self::unbase36($hash);
        $dec = bcmod(bcmul($num, $mmi), $ceil);
    return $dec;
}

}
/// Test
echo "<pre>";

foreach(range(99999999990000, 99999999999999) as $n) {
    echo $n." - ";
    $hash = PseudoCrypt::hash($n, 9);
    echo $hash." - Reversed: ";
    echo PseudoCrypt::unhash($hash)."<br/>";
}

答案 1 :(得分:0)

您不想在下面的代码而不是unbase36(..)中执行base36(..)吗?

public static function unhash36($num, $len = 5) {
    $ceil = pow(36, $len);
    $prime = self::$golden_primes[$len];
    $dec = ($num * $prime)-floor($num * $prime/$ceil)*$ceil;
    $hash = self::base36($dec);                                <=== problem
    return str_pad($hash, $len, “0″, STR_PAD_LEFT);
}

答案 2 :(得分:0)

  

为了能够创建一个6个字符不区分大小写的可逆灰,我使用了我在这里找到的代码:

根据定义,

Hashes不可逆。

  

但是,我无法将哈希值反转为原始值,因为带有base64的类的原始代码确实如此。

Base64不是哈希,它是a method of encoding arbitrary data

您尝试使用不区分大小写的编码解决了哪些问题?这是子域吗?

编辑 - 回答评论:

  

我真正想要的是,但我的目标是将1中的任何整数转换为10,000或8字节的唯一字符串。

这个描述听起来非常接近encrypting URL parameters,这是一种反模式,将来不可避免地会给某些开发者带来严重的痛苦。

除此之外,解决方案非常简单:

$string = base_convert($integer, 10, 36);

然后反过来说:

$integer = base_convert($string, 36, 10);

答案 3 :(得分:0)

我做了另一个算法。

  1. 更易于理解和调整
  2. 仅使用您允许的符号(因此可以轻松区分大小写)
  3. 也是独一无二的
  4. 盒子不区分大小写
  5. 仅用于正整数(用于 ID)
<?php

class PseudoCrypt1
{
    private static $keychars = 'CZPXD5H2FIWB81KE76JY93V4ORLAMT0QSUNG'; // Dictionary of allowed unique symbols, shuffle it for yourself or remove unwanted chars (don't forget to call testParameters after changing)
    private static $divider = 19; // Tune divider for yourself (don't forget to call testParameters after changing)
    private static $biasDivider = 14; // Tune bias divider for yourself (don't forget to call testParameters after changing)
    private static $noise = 23; // Any positive number

    public static function testParameters()
    {
        if (strlen(static::$keychars) < static::$divider + static::$biasDivider - 1) {
            throw new Exception('Check your divider and biasDivider. It must be less than keychars length');
        }
    }

    public static function encode(int $i): string
    {
        if ($i < 0) {
            throw new Exception('Expected positive integer');
        }

        $keychars = static::$keychars;
        $i = $i + static::$noise; // add noise to a number
        $bias = $i % static::$biasDivider;

        $res = '';

        while ($i > 0) {
            $div = $i % static::$divider;
            $i = intdiv($i, static::$divider);
            $res .= $keychars[$div + $bias];
        }

        // Current version of an algorithm is one of these chars (if in the future you will need to identify version)
        // Remember this chars on migrating to new algorithm/parameters
        $res .= str_shuffle('LPTKEZG')[0];
        $res .= $keychars[$bias]; // Encoded bias

        return $res;
    }

    public static function decode($code)
    {
        $keychars = static::$keychars;
        $biasC = substr($code, -1);
        $bias = strpos($keychars, $biasC);
        $code = substr($code, 0, -2);
        $code = str_split(strrev($code));

        $val = 0;

        foreach ($code as $c) {
            $val *= static::$divider;
            $val += strpos($keychars, $c) - $bias;
        }

        return $val - static::$noise;
    }
}

输出

36926 -> 7IWFZX
927331 -> F4WIKP2
9021324 -> AT66R7P1

你可以用这个小测试来测试它(它不包括唯一性测试,但算法是唯一的):

PseudoCrypt1::testParameters();

for ($i = 4000000; $i < 9500000; $i++) {
    $hash = PseudoCrypt1::encode($i);
    echo $i.':'.strlen($hash).':'.$hash.PHP_EOL;
    if ($i != PseudoCrypt1::decode($hash)) {
        echo 'FAIL:'.$i.PHP_EOL;
        die();
    }
}