+ -
当前位置:首页 → 问答吧 → 自己写的一个简单的可逆加密算法

自己写的一个简单的可逆加密算法

时间:2007-09-26

来源:互联网

因为要在php里面加密,flash里面解密,所以准备一个两边都适用的可逆加密算法。
开始打算使用比较经典的AzDGCrypt可逆加密算法,发现在flash使用的时候有个问题,在php中两个相同的值异或之后结果是0,保存到字符串的时候就是chr(0),这个时候这个字符是存在的,长度也为1,而在flash两个相同的值异或之后就彻底消失了,导致解密之后结果不对。无奈之下只好自己写了一个可逆的算法,没有使用异或的。大家帮忙看看这个算法安全性高不高,也就是说在知道算法但不知道key的情况下能否破解?


    /*
     * 加密,可逆
     * 可接受任何字符
     * 安全度非常高
     */
    function encrypt($txt, $key = 'anihc ctI')
    {
        $chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.";
        $ikey ="-x6g6ZWm2G9g_vr0Bo.pOq3kRIxsZ6rm";
        $nh1 = rand(0,64);
        $nh2 = rand(0,64);
        $nh3 = rand(0,64);
        $ch1 = $chars{$nh1};
        $ch2 = $chars{$nh2};
        $ch3 = $chars{$nh3};
        $nhnum = $nh1 + $nh2 + $nh3;
        $knum = 0;$i = 0;
        while(isset($key{$i})) $knum +=ord($key{$i++});
        $mdKey = substr(md5(md5(md5($key.$ch1).$ch2.$ikey).$ch3),$nhnum%8,$knum%8 + 16);
        $txt = base64_encode($txt);
        $txt = str_replace(array('+','/','='),array('-','_','.'),$txt);
        $tmp = '';
        $j=0;$k = 0;
        $tlen = strlen($txt);
        $klen = strlen($mdKey);
        for ($i=0; $i<$tlen; $i++) {
            $k = $k == $klen ? 0 : $k;
            $j = ($nhnum+strpos($chars,$txt{$i})+ord($mdKey{$k++}))%64;
            $tmp .= $chars{$j};
        }
        $tmplen = strlen($tmp);
        $tmp = substr_replace($tmp,$ch3,$nh2 % ++$tmplen,0);
        $tmp = substr_replace($tmp,$ch2,$nh1 % ++$tmplen,0);
        $tmp = substr_replace($tmp,$ch1,$knum % ++$tmplen,0);
        return $tmp;
    }
   
    /*
     * 解密
     *
     */
     function decrypt($txt, $key = 'anihc ctI')
     {
        $chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.";
        $ikey ="-x6g6ZWm2G9g_vr0Bo.pOq3kRIxsZ6rm";
        $knum = 0;$i = 0;
        $tlen = strlen($txt);
        while(isset($key{$i})) $knum +=ord($key{$i++});
        $ch1 = $txt{$knum % $tlen};
        $nh1 = strpos($chars,$ch1);
        $txt = substr_replace($txt,'',$knum % $tlen--,1);
        $ch2 = $txt{$nh1 % $tlen};
        $nh2 = strpos($chars,$ch2);
        $txt = substr_replace($txt,'',$nh1 % $tlen--,1);
        $ch3 = $txt{$nh2 % $tlen};
        $nh3 = strpos($chars,$ch3);
        $txt = substr_replace($txt,'',$nh2 % $tlen--,1);
        $nhnum = $nh1 + $nh2 + $nh3;
        $mdKey = substr(md5(md5(md5($key.$ch1).$ch2.$ikey).$ch3),$nhnum % 8,$knum % 8 + 16);
        $tmp = '';
        $j=0; $k = 0;
        $tlen = strlen($txt);
        $klen = strlen($mdKey);
        for ($i=0; $i<$tlen; $i++) {
            $k = $k == $klen ? 0 : $k;
            $j = strpos($chars,$txt{$i})-$nhnum - ord($mdKey{$k++});
            while ($j<0) $j+=64;
            $tmp .= $chars{$j};
        }
        $tmp = str_replace(array('-','_','.'),array('+','/','='),$tmp);
        return base64_decode($tmp);
    }

//谢谢achun.shx 指正错误,已经做了修改

[ 本帖最后由 sentrychen 于 2008-9-27 16:46 编辑 ]

作者: sentrychen   发布时间: 2007-09-26

为什么没有人顶? 国内就缺乏大量的加密和解密研究人才.


强顶一下表示支持!

作者: londit.cn   发布时间: 2007-09-26

建议有时间多看看关于 RSA DES等算法.

作者: londit.cn   发布时间: 2007-09-26

附上AzDGCrypt的算法
[php]
/**
* Passport 加密函数
*
* @param string 等待加密的原字串
* @param string 私有密匙(用于解密和加密)
*
* @return string 原字串经过私有密匙加密后的结果
*/
function passport_encrypt($txt, $key) {

// 使用随机数发生器产生 0~32000 的值并 MD5()
srand((double)microtime() * 1000000);
$encrypt_key = md5(rand(0, 32000));

// 变量初始化
$ctr = 0;
$tmp = '';

// for 循环,$i 为从 0 开始,到小于 $txt 字串长度的整数
for($i = 0; $i < strlen($txt); $i++) {
// 如果 $ctr = $encrypt_key 的长度,则 $ctr 清零
$ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr;
// $tmp 字串在末尾增加两位,其第一位内容为 $encrypt_key 的第 $ctr 位,
// 第二位内容为 $txt 的第 $i 位与 $encrypt_key 的 $ctr 位取异或。然后 $ctr = $ctr + 1
$tmp .= $encrypt_key[$ctr].($txt[$i] ^ $encrypt_key[$ctr++]);
}

// 返回结果,结果为 passport_key() 函数返回值的 base65 编码结果
return base64_encode(passport_key($tmp, $key));

}

/**
* Passport 解密函数
*
* @param string 加密后的字串
* @param string 私有密匙(用于解密和加密)
*
* @return string 字串经过私有密匙解密后的结果
*/
function passport_decrypt($txt, $key) {

// $txt 的结果为加密后的字串经过 base64 解码,然后与私有密匙一起,
// 经过 passport_key() 函数处理后的返回值
$txt = passport_key(base64_decode($txt), $key);

// 变量初始化
$tmp = '';

// for 循环,$i 为从 0 开始,到小于 $txt 字串长度的整数
for ($i = 0; $i < strlen($txt); $i++) {
// $tmp 字串在末尾增加一位,其内容为 $txt 的第 $i 位,
// 与 $txt 的第 $i + 1 位取异或。然后 $i = $i + 1
$tmp .= $txt[$i] ^ $txt[++$i];
}

// 返回 $tmp 的值作为结果
return $tmp;

}

/**
* Passport 密匙处理函数
*
* @param string 待加密或待解密的字串
* @param string 私有密匙(用于解密和加密)
*
* @return string 处理后的密匙
*/
function passport_key($txt, $encrypt_key) {

// 将 $encrypt_key 赋为 $encrypt_key 经 md5() 后的值
$encrypt_key = md5($encrypt_key);

// 变量初始化
$ctr = 0;
$tmp = '';

// for 循环,$i 为从 0 开始,到小于 $txt 字串长度的整数
for($i = 0; $i < strlen($txt); $i++) {
// 如果 $ctr = $encrypt_key 的长度,则 $ctr 清零
$ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr;
// $tmp 字串在末尾增加一位,其内容为 $txt 的第 $i 位,
// 与 $encrypt_key 的第 $ctr + 1 位取异或。然后 $ctr = $ctr + 1
$tmp .= $txt[$i] ^ $encrypt_key[$ctr++];
}

// 返回 $tmp 的值作为结果
return $tmp;

}
[/php]

在对一个长度为12980字节的字符串中,
我的算法加密和解密一共用了1.1882820129395秒
AzDGCrypt用了1.6753888130188秒

作者: sentrychen   发布时间: 2007-09-26

原帖由 londit.cn 于 2007-9-26 10:57 发表
建议有时间多看看关于 RSA DES等算法.

呵呵。。还没看过,反正我只要求无法破解就行了。

作者: sentrychen   发布时间: 2007-09-26

哈哈,还发现一个好处就是,我的算法加密后,字符串长度为17309,而AzDGCrypt的算法加密后为34616,足足长了一倍。

作者: sentrychen   发布时间: 2007-09-26

那个AzDGCrypt也太弱了点吧…………
lz的那个算法的强度至少比AzDGCrypt好多了。

作者: 神仙   发布时间: 2007-09-26

去网上搜一个叫 PHPRPC 的东东. 可以拿来学习学习

作者: szy_session1987   发布时间: 2007-09-26

lz这个帖子相当不错啊,值得学习

作者: leehui1983   发布时间: 2007-09-26

原帖由 szy_session1987 于 2007-9-26 16:32 发表
去网上搜一个叫 PHPRPC 的东东. 可以拿来学习学习

里面也有一个可逆的加密算法,发出来给大家研究。
[php]

function long2str($v, $w) {
        $len = count($v);
        $n = ($len - 1) << 2;
        if ($w) {
            $m = $v[$len - 1];
            if (($m < $n - 3) || ($m > $n)) return false;
            $n = $m;
        }
        $s = array();
        for ($i = 0; $i < $len; $i++) {
            $s[$i] = pack("V", $v[$i]);
        }
        if ($w) {
            return substr(join('', $s), 0, $n);
        }
        else {
            return join('', $s);
        }
    }

    function str2long($s, $w) {
        $v = unpack("V*", $s. str_repeat("\0", (4 - strlen($s) % 4) & 3));
        $v = array_values($v);
        if ($w) {
            $v[count($v)] = strlen($s);
        }
        return $v;
    }
    function int32($n) {
        while ($n >= 2147483648) $n -= 4294967296;
        while ($n <= -2147483649) $n += 4294967296;
        return (int)$n;
    }
    function xxtea_encrypt($str, $key) {
        if ($str == "") {
            return "";
        }
        $v = str2long($str, true);
        $k = str2long($key, false);
        if (count($k) < 4) {
            for ($i = count($k); $i < 4; $i++) {
                $k[$i] = 0;
            }
        }
        $n = count($v) - 1;
        $z = $v[$n];
        $y = $v[0];
        $delta = 0x9E3779B9;
        $q = floor(6 + 52 / ($n + 1));
        $sum = 0;
        while (0 < $q--) {
            $sum = int32($sum + $delta);
            $e = $sum >> 2 & 3;
            for ($p = 0; $p < $n; $p++) {
                $y = $v[$p + 1];
                $mx = int32((($z >> 5 & 0x07ffffff) ^ $y << 2) + (($y >> 3 & 0x1fffffff) ^ $z << 4)) ^ int32(($sum ^ $y) + ($k[$p & 3 ^ $e] ^ $z));
                $z = $v[$p] = int32($v[$p] + $mx);
            }
            $y = $v[0];
            $mx = int32((($z >> 5 & 0x07ffffff) ^ $y << 2) + (($y >> 3 & 0x1fffffff) ^ $z << 4)) ^ int32(($sum ^ $y) + ($k[$p & 3 ^ $e] ^ $z));
            $z = $v[$n] = int32($v[$n] + $mx);
        }
        return long2str($v, false);
    }
    function xxtea_decrypt($str, $key) {
        if ($str == "") {
            return "";
        }
        $v = str2long($str, false);
        $k = str2long($key, false);
        if (count($k) < 4) {
            for ($i = count($k); $i < 4; $i++) {
                $k[$i] = 0;
            }
        }
        $n = count($v) - 1;
        $z = $v[$n];
        $y = $v[0];
        $delta = 0x9E3779B9;
        $q = floor(6 + 52 / ($n + 1));
        $sum = int32($q * $delta);
        while ($sum != 0) {
            $e = $sum >> 2 & 3;
            for ($p = $n; $p > 0; $p--) {
                $z = $v[$p - 1];
                $mx = int32((($z >> 5 & 0x07ffffff) ^ $y << 2) + (($y >> 3 & 0x1fffffff) ^ $z << 4)) ^ int32(($sum ^ $y) + ($k[$p & 3 ^ $e] ^ $z));
                $y = $v[$p] = int32($v[$p] - $mx);
            }
            $z = $v[$n];
            $mx = int32((($z >> 5 & 0x07ffffff) ^ $y << 2) + (($y >> 3 & 0x1fffffff) ^ $z << 4)) ^ int32(($sum ^ $y) + ($k[$p & 3 ^ $e] ^ $z));
            $y = $v[0] = int32($v[0] - $mx);
            $sum = int32($sum - $delta);
        }
        return long2str($v, true);
    }
[/php]

呵呵,这是xxtea的可逆加密算法,代码比较复杂,看起来头痛。我用一个字节数为2万的字符串测试了一下,还是我写的算法速度快。
//测试结果
加密字符串字节数:20000
ntk算法加密后字符串长度:26669 加密所用时间:1.2221281528473 秒
解密所用时间:1.4950909614563 秒合计:2.7172191143036 秒
xxtea算法加密后字符串长度:20004 加密所用时间:4.4340929985046 秒
解密所用时间:4.4368131160736 秒合计:8.8709061145782 秒

作者: sentrychen   发布时间: 2007-09-26

不错不错,收藏了!

作者: dx_andy   发布时间: 2007-09-27

对加密没有研究,不过看了你的代码
[php]
$i=0;$j=0;$k = 0;
for ($i=0; $i<strlen($txt); $i++) {
  $k = $k == strlen($mdKey) ? 0 : $k;
  $j = ($nh+strpos($chars,$txt[$i])+ord($mdKey[$k]))%64;
  $tmp .= $chars[$j];
}

[/php]
那岂不是$k永远都是0,等价于
[php]
$i=0;$j=0;
for ($i=0; $i<strlen($txt); $i++) {
  $j = ($nh+strpos($chars,$txt[$i])+ord($mdKey[0]))%64;
  $tmp .= $chars[$j];
}

[/php]
我不熟悉加密,不过凭感觉
这个强度就不怎么样了吧!

作者: achun.shx   发布时间: 2007-09-27

原帖由 achun.shx 于 2007-9-27 10:15 发表
对加密没有研究,不过看了你的代码
[php]
$i=0;$j=0;$k = 0;
for ($i=0; $i

呵呵,谢谢指正bug,我没有仔细检查代码,正确的应该是
$mdKey[$k++]的,再次谢谢

作者: sentrychen   发布时间: 2007-09-27

加/解密这块没接触过,或者说接触不深,收藏。。。

作者: l80d   发布时间: 2007-09-27

原帖由 sentrychen 于 2007-9-26 20:51 发表

呵呵,这是xxtea的可逆加密算法,代码比较复杂,看起来头痛。我用一个字节数为2万的字符串测试了一下,还是我写的算法速度快。
//测试结果
加密字符串字节数:20000
ntk算法加密后字符串长度:26669 加密所用时间:1.2221281528473 秒
解密所用时间:1.4950909614563 秒合计:2.7172191143036 秒
xxtea算法加密后字符串长度:20004 加密所用时间:4.4340929985046 秒
解密所用时间:4.4368131160736 秒合计:8.8709061145782 秒
   ...


xxtea 有个 for PHP 的 PECL(c语言的实现)扩展,速度比用纯 PHP 的实现快的多得多,纯 PHP 实现速度慢的原因是 PHP 没有无符号整形数,所以,需要对数字进行校正,这个运算消耗了大部分的时间,其它语言没有这个问题。所以,PHP 如果用 xxtea 算法,最好是使用它的 PECL 扩展,而不是它的纯 PHP 实现。

作者: andot   发布时间: 2007-10-26

mark

作者: ajaxer   发布时间: 2008-08-25

我忘记在哪里看过一篇文章,说加密和解密的时间随机数有很大的关系,楼主的$nh = rand(0,64);是0-64之间,而AzDGCrypt的算法$encrypt_key = md5(rand(0, 32000));在0-32000之间,肯定楼主的快咯=。= tukiz29 ,个人见解~~说错别笑话

作者: kindge   发布时间: 2008-08-25

原帖由 kindge 于 2008-8-25 18:42 发表
我忘记在哪里看过一篇文章,说加密和解密的时间随机数有很大的关系,楼主的$nh = rand(0,64);是0-64之间,而AzDGCrypt的算法$encrypt_key = md5(rand(0, 32000));在0-32000之间,肯定楼主的快咯=。= tukiz29 ,个人见 ...

呵呵,这个是之前写的,现在我已经修改了。对于每一个加密的字符串,能生成26万多种不同的加密结果,并且每一种结果都能解密回来,效率也高了很多。加密一个2万长度的字符串和解密加起来只需要0.3秒左右。修改后的算法还使得字符可以安全用于url传输,不用考虑urlencode的问题。
呵呵,对于这次修改后的算法,我可以毫不夸张的说,即使是世界上技术最好的高手在不知道key的情况下也无法破解我的加密算法。当然如果知道了key,就直接用我的函数就能解密了。

    /*
     * 加密,可逆
     * 可接受任何字符
     * 安全度非常高
     */
    function encrypt($txt, $key = 'anihc ctI')
    {
        $chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.";
        $ikey ="-x6g6ZWm2G9g_vr0Bo.pOq3kRIxsZ6rm";
        $nh1 = rand(0,64);
        $nh2 = rand(0,64);
        $nh3 = rand(0,64);
        $ch1 = $chars{$nh1};
        $ch2 = $chars{$nh2};
        $ch3 = $chars{$nh3};
        $nhnum = $nh1 + $nh2 + $nh3;
        $knum = 0;$i = 0;
        while(isset($key{$i})) $knum +=ord($key{$i++});
        $mdKey = substr(md5(md5(md5($key.$ch1).$ch2.$ikey).$ch3),$nhnum%8,$knum%8 + 16);
        $txt = base64_encode($txt);
        $txt = str_replace(array('+','/','='),array('-','_','.'),$txt);
        $tmp = '';
        $j=0;$k = 0;
        $tlen = strlen($txt);
        $klen = strlen($mdKey);
        for ($i=0; $i<$tlen; $i++) {
            $k = $k == $klen ? 0 : $k;
            $j = ($nhnum+strpos($chars,$txt{$i})+ord($mdKey{$k++}))%64;
            $tmp .= $chars{$j};
        }
        $tmplen = strlen($tmp);
        $tmp = substr_replace($tmp,$ch3,$nh2 % ++$tmplen,0);
        $tmp = substr_replace($tmp,$ch2,$nh1 % ++$tmplen,0);
        $tmp = substr_replace($tmp,$ch1,$knum % ++$tmplen,0);
        return $tmp;
    }
   
    /*
     * 解密
     *
     */
     function decrypt($txt, $key = 'anihc ctI')
     {
        $chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.";
        $ikey ="-x6g6ZWm2G9g_vr0Bo.pOq3kRIxsZ6rm";
        $knum = 0;$i = 0;
        $tlen = strlen($txt);
        while(isset($key{$i})) $knum +=ord($key{$i++});
        $ch1 = $txt{$knum % $tlen};
        $nh1 = strpos($chars,$ch1);
        $txt = substr_replace($txt,'',$knum % $tlen--,1);
        $ch2 = $txt{$nh1 % $tlen};
        $nh2 = strpos($chars,$ch2);
        $txt = substr_replace($txt,'',$nh1 % $tlen--,1);
        $ch3 = $txt{$nh2 % $tlen};
        $nh3 = strpos($chars,$ch3);
        $txt = substr_replace($txt,'',$nh2 % $tlen--,1);
        $nhnum = $nh1 + $nh2 + $nh3;
        $mdKey = substr(md5(md5(md5($key.$ch1).$ch2.$ikey).$ch3),$nhnum % 8,$knum % 8 + 16);
        $tmp = '';
        $j=0; $k = 0;
        $tlen = strlen($txt);
        $klen = strlen($mdKey);
        for ($i=0; $i<$tlen; $i++) {
            $k = $k == $klen ? 0 : $k;
            $j = strpos($chars,$txt{$i})-$nhnum - ord($mdKey{$k++});
            while ($j<0) $j+=64;
            $tmp .= $chars{$j};
        }
        $tmp = str_replace(array('-','_','.'),array('+','/','='),$tmp);
        return trim(base64_decode($tmp));
    }


[ 本帖最后由 sentrychen 于 2008-8-30 17:46 编辑 ]

作者: sentrychen   发布时间: 2008-08-30

修改了一个bug,原来无法加密空字符,现在空字符也可以加密。。。不过空字符就算加密了还是可以从长度上猜出来的。:sweat: :sweat: :sweat:

作者: sentrychen   发布时间: 2008-08-30

学习了

作者: fatpotato   发布时间: 2008-08-30

厉害,学习了

作者: renlang119   发布时间: 2008-09-20

咋看不到源代码呢?
奇了怪了!

作者: fly1983   发布时间: 2008-09-27

有人做ioncube的解密么?

作者: fatpotato   发布时间: 2008-09-27

原帖由 fly1983 于 2008-9-27 12:35 发表
咋看不到源代码呢?
奇了怪了!

奇怪,我也看不到

作者: sentrychen   发布时间: 2008-09-27

编码牛,俺要学习,学习。
纯顶。

作者: imkow   发布时间: 2008-09-27

请楼主谈谈自己的算法相对与其他算法的优势?夸一夸宝!

作者: imkow   发布时间: 2008-09-27

原帖由 imkow 于 2008-9-27 16:00 发表
请楼主谈谈自己的算法相对与其他算法的优势?夸一夸宝!

夸耀就没有必要了,其实我对加密没有什么研究,也没看过什么加密理论,因此不敢在众多安全专家面前班门弄斧。我还是简单说说这个算法的原理吧,真的很简单,完全是md5的功劳。
首先,我根据公key、私key和一个rand(64×64×64)的随机数,利用md5函数制造了一个字符串序列。
然后将要加密的字符串转为base64编码。
再根据这个字符串序列的字符位置对base64编码后的字符串进行逐个字符替换。
最后根据公key的字符ascii码之和将几个随机因子插入到替换后的字符串里面。
解密就是倒过来运算。


如果说这个算法的一点优势,那就是速度比AzDGCrypt和xxtea的算法快,然后加密后的字符长度相当于AzDGCrypt的1/2,但比xxtea的长了大概1/4。另外加密后的字符串在url里面传送很稳定,不会被转义。安全性方面就很难比较了,我设置了一个公key(在函数调用传入),一个私key,直接写在函数里面。如果不知道这两个key还是很难破解的。

作者: sentrychen   发布时间: 2008-09-27

厉害!学习了

初学PHP,代码不懂

加密那里好像有点问题,多刷新几次,有时会有运行错误,加密中文时好像多点

作者: snxu   发布时间: 2008-11-26

加密解密没有你想得那么简单的!

作者: liexusong   发布时间: 2008-11-26

不知道是哪位专家说“没有破译不了的密码”。我觉得对于安全性要求不高的情况没必要过分追求加密算法的可靠性。

作者: xudongding   发布时间: 2008-12-11