UCenter (五) 加密

任何想整合UCenter的应用,而采用HTTP请求的方式来获取通信数据的话,必须要把这个请求的URL参数经过一定的方式加密。并且要在UCenter的application表里要注明加密时用的UC_KEY是什么,这样UCenter才可以把密文解密成明文。UCenter并不接受直接的明文传参。

        加上我的注解之后,算法如下,代码还是有点长的,看不下去的话直接转到结尾看总结吧:

<?php
/**
*
* 加密和解密
* @param string $string 加密时的明文和解密时的密文
* @param string $operation 为DECODE时为解密操作,其他值则为加密操作
* @param string $key 加密或解密时用到的key
* @param int $expiry 密文的有效时限,单位是秒
*/
function authcode($string, $operation = ‘DECODE’, $key = ”, $expiry = 0) {
/**
*
* 确定了密文的随机程序,设为0的话,会根据明文产生固定密文
* @var int
*/
$ckey_length = 4;
/**
*
* 如果没有设置key,会在配置文件里读取默认值
* 把key经过md5之后产生新的key
* @var string
*/
$key = md5($key ? $key : $GLOBALS [‘discuz_auth_key’]);
$keya = md5(substr($key, 0, 16));
$keyb = md5(substr($key, 16, 16));
/**
* 加密时:为microtime()用md5加密之后的后$ckey_length位,可知这是个随时间改变而改变的值,生成随机密文靠$keyc
* 解密时:为密文的$ckey_length开始到最后一位,可知密文的前$ckey_length位与直正的明文是没有关系的
*/
$keyc = $ckey_length ? ($operation == ‘DECODE’ ? substr($string, 0, $ckey_length) : substr(md5(microtime()), -$ckey_length)) : ”;
/**
* $cryptkey在后面会用于生成密码薄排序数组,
* 这步之后剩下会用到的是$cryptkey,$keyb,$keyc
* $key和$keya只是过渡,产生$cryptkey之后就没用了
*/
$cryptkey = $keya . md5($keya . $keyc);

$key_length = strlen($cryptkey);
/**
*
* 加密时:第一步加密,在明文前面加上了10位数字和16位由$keyb参于的密文。
* 前10位是纯数字,用于超时验证,后16位是md5后的密文是用于完整性验证的。
* 生成的字符串里可以找到完整的明文,所以也不算是真正的加密
* 解密时:第一步解密,去掉密文的前$ckey_length个字符,再用base64_decode
* 因为在加密的最后一步就在生成的密文前加上$ckey后再base64_encode
* 解密是加密的逆过程,所以加密时的最后一步就是解密时的第一步
* @var string
*/
$string = $operation == ‘DECODE’ ? base64_decode(substr($string, $ckey_length)) : sprintf(‘%010d’, $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb), 0, 16) . $string;
$string_length = strlen($string);

$result = ”;
$box = range(0, 255);
/**
*
* $rndkey 通过对cryptkey各个字符的ascii码生成
* 用于给密码薄$box排序
* @var array
*/
$rndkey = array();
for ($i = 0; $i <= 255; $i++) {
   $rndkey [$i] = ord($cryptkey [$i % $key_length]);
}
/**
* $box本来是0-255有序的数组
* 这里用$rndkey把$box数组里的顺序打乱了
* 而这个密码薄$box才是真正加密解的依据
* 其实不经过这个排序,有序的$box也可以作为密码本用于加解密
* 只是这样的话,key就没有作用了,知道算法的话,谁都可以破解了
*/
for ($j = $i = 0; $i < 256; $i++) {
   $j = ($j + $box [$i] + $rndkey [$i]) % 256;
   $tmp = $box [$i];
   $box [$i] = $box [$j];
   $box [$j] = $tmp;
}
/**
* 真正的用异或算法加密的过程
*/
for ($a = $j = $i = 0; $i < $string_length; $i++) {
   $a = ($a + 1) % 256;
   $j = ($j + $box [$a]) % 256;
   $tmp = $box [$a];
   $box [$a] = $box [$j];
   $box [$j] = $tmp;
   $result .= chr(ord($string [$i]) ^ ($box [($box [$a] + $box [$j]) % 256]));
}
/**
* 加密时:返回密文
* 解密时:成功返回明文,失败返回空字符串
*/
if ($operation == ‘DECODE’) {
   if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) – time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16)) {
    return substr($result, 26);
   } else {
    return ”;
   }
} else {
   return $keyc . str_replace(‘=’, ”, base64_encode($result));
}

}

         加密总的过程就是把明文的各个字符的ascii码跟密码薄里相应的位置异或运行之后产生新的ascii码,再用chr函数取得新的ascii码对应的字符串,最后用base65_encode包装下外形,不包装的话得到的字符串是一个非常奇怪的字符串。举例来说:

明文$string为:1234,经过一系统的转化之后,密码本$box是255-0的倒序数组,密码本$box是固定0-255共有256个数字组成的数组,只是顺序由key经过一系列运算之后产生的$rndkey来决定的,这里假设正好产生的$box为255-0的倒序排列。

那1的密文的产生过程中:
$a = 1;$j = $box[$a] % 256 = 254;
1的密文就是:
chr(ord(1) ^ $box[($box[$a]+$box[$j]) % 256]))
= chr(49 ^ $box[(254+1)%265 ]))
= chr(49 ^ $box[255])
= chr(49 ^ 0)
= chr(49)
= 1
1的密文正好是chr(49),也就是1。
后面的234经过同样的$box和同样的步骤可以得到
2的密文为chr(204) //我查了下ascii码表,一个奇怪的字符,不管了
3的密文为chr(200)
4的密文为chr(195)
chr(204)这些好像是个奇怪的字符,反正运用的时候也可以不管具体的是什么字符,因为最后可以用个可逆的加密函数 base65_encode再包装下。
最终结果就是:MczIw8fa

对密文Mczlw8fa解密转化为明文的话。可以先进行 base65_decode之后得到 1.chr(204).chr(200).chr(195) 这个字符串之后,通过同样的$box和同样的运算之后会得到明文1234。

异或算法就是这么一种特性,所以普遍运用于加密解密的算法中,只是怎么个异或用法,却是各个算法因程序而异了。另附,异或算法解释:

例如 2 ^ 4 = ? 2的二进制是 010,4的二进制是 100 。
010
^100
=110
坚着看,相同得0,相异得1。把110转化成十进制就是6
那么 6 ^ 4 = ? 同样转化为二进制运行,再坚着看
110
^100
=010
010转化为十进制为 2

结果可知如果 a ^ b = c,那么 c ^ b 必然会等于a 。(其中a,b,c均为数字)

在加密解密的应用上就是 明文 ^ KEY = 密文,密文 ^ KEY = 明文了。

难题就出在这个KEY的处理上,UCenter中运用的这个算法是非常具有经典意义的。学习完这个之后,也许就可以自己写加密算法了。应该自己试试,不是吗?

您可能还喜欢...