首页 > Javascript > DiscuzX中uc_authcode函数js版本,配合php版双向加密解密

DiscuzX中uc_authcode函数js版本,配合php版双向加密解密

上周准备写一个js版的uc_authcode函数,根据网上整理的资料:http://bbs.csdn.net/topics/390310377?page=1中Frogant的源码(纯JS版)进行相关测试。

同时引入了md5.js和base64.js库。测试结果的准确率不到一半,于是根据DX中的uc_authcode源码进行跟踪调试,发现问题出在base64的编码上,可能是中外对字符串的编码不一致导致的。

后来在网上搜到其他版本的base64.js库测试可用:http://download.csdn.net/detail/duyipeng/2034266上传的资源可以测试通过。

注意:无论是js还是php的脚本文件的编码必须为utf-8编码,否则可能会出现错误。原版的代码在IE下兼容有一些问题,经过修改之后目前可兼容IE6,7,8,opera,firefox,google等浏览器。源代码如下:

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <title></title>
        <style type="text/css">
        </style>
    </head>
    <body>
		<?php
		if(isset($_GET['jsCode']))
		{
			//传入的字符串
			$jsCode = base64_decode($_GET['jsCode']);
			p('jsCode:'.$jsCode);
			//解码后的字符串
			$decode = uc_authcode($jsCode, 'DECODE', 'key');
			p('解码后:'.$decode);
			//urldecode之后的字符串
			p('urldecode后:'.urldecode($decode));
		}else{
			$en = '~!@#$%^&*()_+=-0987654321\][.,/';
			$en_code = uc_authcode($en, 'ENCODE', 'key');
			p(uc_authcode($en_code, 'DECODE', 'key'));

			$cn = '屌丝(联通)';
			$cn_code = uc_authcode($cn, 'ENCODE', 'key');
			p(uc_authcode($cn_code, 'DECODE', 'key'));

			$str = urlencode('来自php加密');
			$encode = uc_authcode($str, 'ENCODE', 'key');
		}


		function p($var)
		{
			echo "<pre>";
			if($var === false)
			{
				echo 'false';
			}else if($var === ''){
				print_r("''");
			}else{
				print_r($var);
			}
			echo "</pre>";
		}

		/**
		* @param    string      $string 加密内容
		* @param    string      $operation 加密动作
		* @param    string      $key 私钥
		* @param    int         $expiry 有效时间秒
		* @return   string      加密串
		*/
		function uc_authcode($string, $operation = 'DECODE', $key = '', $expiry = 0)
		{
			$ckey_length = 4;
			$key = md5($key);
			$keya = md5(substr($key, 0, 16));
			$keyb = md5(substr($key, 16, 16));
			$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
			$cryptkey = $keya.md5($keya.$keyc);
			$key_length = strlen($cryptkey);
			$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 = array();
			for($i = 0; $i <= 255; $i++)
			{
				$rndkey[$i] = ord($cryptkey[$i % $key_length]);
			}
			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));
			}
		}

		?>
        <div id="resultDiv"><?php echo $encode;?></div>
        <script type="text/javascript" src="md5.min.js"></script>
        <script type="text/javascript" src="base64.min.js"></script>
        <script type="text/javascript">
            /**  
            * @param    string      str 加密内容  
            * @param    string      operation 加密动作  
            * @param    string      key 密钥  
            * @param    int         expiry 有效时间秒  
            * @return   string      加密串  
            */  
            function uc_authcode(str, operation, key, expiry) {
                var operation = operation ? operation : 'DECODE';
                var key = key ? key : '';
                var expiry = expiry ? expiry : 0;

                var ckey_length = 4;
                key = md5(key);

                // 密匙a会参与加解密
                var keya = md5(key.substr(0, 16));
                // 密匙b会用来做数据完整性验证
                var keyb = md5(key.substr(16, 16));
                // 密匙c用于变化生成的密文
                // IE下不支持substr第一个参数为负数的情况
                if(ckey_length){
                    if(operation == 'DECODE'){
                        var keyc = str.substr(0, ckey_length);
                    }else{
                        var md5_time = md5(microtime());
                        var start = md5_time.length - ckey_length;
                        var keyc = md5_time.substr(start, ckey_length)
                    }
                }else{
                    var keyc = '';
                }
                // 参与运算的密匙
                var cryptkey = keya + md5(keya + keyc);

                var strbuf;

                if (operation == 'DECODE') {
                    str = str.substr(ckey_length);
                    strbuf = base64_decode(str);
                    //string = b.toString();
                } else {
                    expiry = expiry ? expiry + time() : 0;
                    tmpstr = expiry.toString();
                    if (tmpstr.length >= 10)
                        str = tmpstr.substr(0, 10) + md5(str + keyb).substr(0, 16) + str;
                    else {
                        var count = 10 - tmpstr.length;
                        for (var i = 0; i < count; i++) {
                            tmpstr = '0' + tmpstr;
                        }
                        str = tmpstr + md5(str + keyb).substr(0, 16) + str;
                    }
                    strbuf = str;
                }
                

                var box = new Array(256);
                for (var i = 0; i < 256; i++) {
                    box[i] = i;
                }
                var rndkey = new Array();
                // 产生密匙簿
                for (var i = 0; i < 256; i++) {
                    rndkey[i] = cryptkey.charCodeAt(i % cryptkey.length);
                }
                // 用固定的算法,打乱密匙簿,增加随机性,好像很复杂,实际上对并不会增加密文的强度
                for (var j = i = 0; i < 256; i++) {
                    j = (j + box[i] + rndkey[i]) % 256;
                    tmp = box[i];
                    box[i] = box[j];
                    box[j] = tmp;
                }

                // 核心加解密部分
                var s = '';
                //IE下不支持直接通过下标访问字符串的字符,需要先转换为数组
                strbuf = strbuf.split('');
                for (var a = j = i = 0; i < strbuf.length; i++) {
                    a = (a + 1) % 256;
                    j = (j + box[a]) % 256;
                    tmp = box[a];
                    box[a] = box[j];
                    box[j] = tmp;
                    // 从密匙簿得出密匙进行异或,再转成字符
                    s += chr(ord(strbuf[i])^(box[(box[a] + box[j]) % 256]));
                }
                
                if (operation == 'DECODE') {
                    if ((s.substr(0, 10) == 0 || s.substr(0, 10) - time() > 0) && s.substr(10, 16) == md5(s.substr(26) + keyb).substr(0, 16)) {
                        s = s.substr(26);
                    } else {
                        s = '';
                    }
                } else {
                    s = base64_encode(s);
                    var regex = new RegExp('=', "g");
                    s = s.replace(regex, '');
                    s = keyc + s;
                }
                
                return s;
            }
            
            function time() {
                var unixtime_ms = new Date().getTime();
                return parseInt(unixtime_ms / 1000);
            }

            function microtime(get_as_float) {
                var unixtime_ms = new Date().getTime();
                var sec = parseInt(unixtime_ms / 1000);
                return get_as_float ? (unixtime_ms / 1000) : (unixtime_ms - (sec * 1000)) / 1000 + ' ' + sec;
            }
            function chr(s) {
                return String.fromCharCode(s);
            }
            function ord(s) {
                return s.charCodeAt();
            }

            function md5(str) {
                return hex_md5(str);
            }
            
            var resultDiv = document.getElementById('resultDiv');
            var str = resultDiv.innerHTML;
            //php加密,js解密
            document.write(uc_authcode(str, 'DECODE', 'key')+'<br/>');
            //使用错误密钥将会得到空字符串
            document.write(uc_authcode(str, 'DECODE', 'errorkey')+'<br/>');
            //英文测试,js加密解密
            var en = '~!@#$%^&*()_+=-0987654321\][.,"\'></abcdefghijklmnopqrstuvwxyz';
            var en_code = uc_authcode(en, 'ENCODE', 'key');
            document.write(uc_authcode(en_code, 'DECODE', 'key')+'<br/>');
            //中文测试,js加密解密,特殊编码字符串需要通过手动进行encodeURI编码解码,暂未发现异常
            var cn = encodeURI('屌丝联通营业(),。?“:》《%……');
            var cn_code = uc_authcode(cn, 'ENCODE', 'key');
            document.write(cn_code+'<br/>');
            resultDiv.innerHTML = '<a href="?jsCode='+base64_encode(cn_code)+'">点我使用php解码</a>';
        </script>
    </body>
</html>

经过反复尝试,中文字符未采用duyipeng上传的base64.js中的utf16to8编码方式,而是采用js原生的encodeURI进行编码,在php端同样可以使用urlencode进行编码。最后进行双向测试,目前进行测试的字符全部加密解密成功,由此可以实现js和php进行无缝对接。


注意:在使用GET方式传参过程中,经过uc_authcode之后的加密串可能会带有空格等特殊字符,需要使用js版base64_encode进行编码后传输,在php端使用php版base64_decode进行解码,然后使用uc_authcode进行解密。


源码下载地址:http://download.csdn.net/detail/zhengshuiguang/8289509


本文地址:http://blog.zhengshuiguang.com/js/uc-authcode.html

转载随意,但请附上文章地址:-)

标签:uc_authcode discuzx 加密 解密 兼容ie

相关文章

评论已关闭