工作记录:DiscuzX中uc_authcode函数js版本,配合java双向加密解密

来源:互联网 发布:adobe ae 2015 mac 编辑:程序博客网 时间:2024/06/04 19:18

上周准备写一个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、java还是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><?phpif(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

同样,java在编码中文字符的时候建议使用URLEncoder和URLDecoder进行编码和解码再加密。不过,本类中的encode和decode只是js的移植版,不保证绝对兼容,之所以这么做是因为直接使用org.apache.commons.codec.binary.Base64等编码和解码的时候遇到了其他语言加密java可以解密但是java加密后js或php都无法解密。

此java类在小数据量情况下基本上不会造成性能问题,大量数据通信暂未测试。(小范围内测通过)

代码如下:

package com.github.shuiguang.utils;import java.io.IOException;import java.io.UnsupportedEncodingException;import java.net.URLDecoder;import java.net.URLEncoder;import java.security.MessageDigest;import java.util.Date;/** * uc_authcode工具类 * @author shuiguang * */public class UCUtil {public static final String base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";public static void main(String[] args) throws InterruptedException, IOException {String operation = "DECODE";String key = "key";int expiry = 1;String ucAuthcode = UCUtil.ucAuthcode(URLEncoder.encode("我的世界", "UTF-8"), "ENCODE", key, expiry);// 打印出密文System.out.println(ucAuthcode);String result2 = UCUtil.ucAuthcode(ucAuthcode, operation, key, expiry);// 密文未过期可以打印出明文System.out.println("result2="+URLDecoder.decode(result2, "UTF-8"));// 休眠1s后密文过期Thread.sleep(1000);String result3 = UCUtil.ucAuthcode(ucAuthcode, operation, key, expiry);// 密文过期后打印出空字符串System.out.println("result3="+URLDecoder.decode(result3, "UTF-8"));}/** * 加密算法 * @param str 待加密内容 * @param operation 加密动作 * @param key 密钥 * @param expiry 有效时间,单位s * @return 加密后的字符串 * @throws UnsupportedEncodingException  */public static String ucAuthcode(String str, String operation, String key, int expiry) throws UnsupportedEncodingException {int ckey_length = 4;String defaultCharset = "UTF-8";key = md5(key, defaultCharset);// 密匙a会参与加解密String keya = md5(key.substring(0, 16), defaultCharset);// 密匙b会用来做数据完整性验证String keyb = md5(key.substring(16, 32), defaultCharset);String keyc;// 密匙c用于变化生成密文keyc = "";if(ckey_length > 0) {if("DECODE".equals(operation)) {keyc = str.substring(0, ckey_length);}else{String md5_time = md5(microtime(), defaultCharset);int start = md5_time.length() - ckey_length;keyc = md5_time.substring(start, start+ckey_length);}}// 参与运算的密匙String cryptkey = keya + md5(keya + keyc, defaultCharset);String strbuf;if("DECODE".equals(operation)) {str = str.substring(ckey_length);strbuf = decode(str);}else{expiry = expiry > 0 ? expiry + time() : 0;String tmpstr = expiry + "";if(tmpstr.length() >= 10) {str = tmpstr.substring(0, 10) + md5(str + keyb, defaultCharset).substring(0, 16) + str;}else{int count = 10 - tmpstr.length();for (int i = 0; i < count; i++) {tmpstr = "0"+tmpstr;}str = tmpstr + md5(str + keyb, defaultCharset).substring(0, 16) + str;}strbuf = str;}int[] box = new int[256];for (int i = 0; i < box.length; i++) {box[i] = i;}char[] rndkey = new char[256];for (int i = 0; i < rndkey.length; i++) {rndkey[i] = cryptkey.charAt(i % cryptkey.length());}// 用固定算法打乱密匙薄,增加随机性,好像很复杂,实际上并不会增加密文的强度for (int i = 0, j = 0; i < 256; i++) {j = (j + box[i] + rndkey[i]) % 256;int tmp = box[i];box[i] = box[j];            box[j] = tmp;}// 核心加密解密部分String s = "";char[] charArray = strbuf.toCharArray();for (int a = 0, i = 0, j = 0; i < charArray.length; i++) {a = (a + 1) % 256;j = (j + box[a]) % 256;int tmp = box[a];box[a] = box[j];box[j] = tmp;// 从密匙薄得出密匙进行异或,再转成字符char c = (char) (charArray[i]^box[(box[a] + box[j]) % 256]);s += c + "";}if("DECODE".equals(operation)) {int prefix;// 与js差异之一try{prefix = Integer.parseInt(s.substring(0, 10));}catch(Exception e) {prefix = 0;}if(("0000000000".equals(s.substring(0, 10)) || prefix - time() > 0) && md5(s.substring(26) + keyb, defaultCharset).substring(0, 16).equals(s.substring(10, 26))) {s = s.substring(26);}else{s = "";}}else{s = encode(s);s = s.replaceAll("=", "");s = keyc + s;}return s;}/** * 32位 md5 加密  * @author Z * @param s * @param charset * @return String 加密后的32位长度的字符串 */public final static String md5(String s, String charset) {        try {            byte[] btInput = s.getBytes(charset);            MessageDigest mdInst = MessageDigest.getInstance("MD5");            mdInst.update(btInput);            byte[] md = mdInst.digest();            StringBuffer sb = new StringBuffer();            for (int i = 0; i < md.length; i++) {                int val = ((int) md[i]) & 0xff;                if (val < 16){                    sb.append("0");                }                sb.append(Integer.toHexString(val));            }            return sb.toString();        } catch (Exception e) {            return null;        }    }/** * 返回当前微秒数 * @return */public static String microtime() {long unixtime_ms = new Date().getTime();int sec = (int) unixtime_ms / 1000;return (unixtime_ms - (sec * 1000)) / 1000 + " " + sec;}/** * 获取当前Unix时间秒数 * @return */public static int time() {long time = new Date().getTime()/1000;return (int) time;}/** * base64编码(纯英文) * @param str * @return */private static String encode(String str) {String out = "";int i = 0;int len = 0;int c1, c2, c3;len = str.length(); while(i < len) {c1 = (int) str.charAt(i++) & 0xff;     if(i == len)     {         out += base64EncodeChars.charAt(c1 >> 2);         out += base64EncodeChars.charAt((c1 & 0x3) << 4);         out += "==";         break;     }     c2 = (int) str.charAt(i++);     if(i == len)     {        out += base64EncodeChars.charAt(c1 >> 2);         out += base64EncodeChars.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));         out += base64EncodeChars.charAt((c2 & 0xF) << 2);         out += "=";         break;     }     c3 = (int) str.charAt(i++);     out += base64EncodeChars.charAt(c1 >> 2);     out += base64EncodeChars.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));     out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6));     out += base64EncodeChars.charAt(c3 & 0x3F); }return out;}/** * base64解码(纯英文) * @param str * @return */private static String decode(String str) {int[] base64DecodeChars = new int[]{     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,     52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,     -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,     15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,     -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,     41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1}; int c1, c2, c3, c4;int i = 0;int len = 0;String out = "";len = str.length();while(i < len) {     /* c1 */     do {         c1 = base64DecodeChars[(int) str.charAt(i++) & 0xff];     } while(i < len && c1 == -1);     if(c1 == -1)         break;     /* c2 */     do {         c2 = base64DecodeChars[(int) str.charAt(i++) & 0xff];     } while(i < len && c2 == -1);     if(c2 == -1)         break;     out += (char) ((c1 << 2) | ((c2 & 0x30) >> 4)) + "";     /* c3 */     do {         c3 = ((int) str.charAt(i++) & 0xff);         if(c3 == 61)         return out;         c3 = base64DecodeChars[c3];     } while(i < len && c3 == -1);     if(c3 == -1)         break;     out += (char) (((c2 & 0XF) << 4) | ((c3 & 0x3C) >> 2)) + "";     /* c4 */     do {    // 与js差异之二    try{    c4 = (int) str.charAt(i++) & 0xff;        if(c4 == 61)         return out;         c4 = base64DecodeChars[c4];     } catch (Exception e) {    c4 = -1;    }    } while(i < len && c4 == -1);     if(c4 == -1)         break;     out += (char) (((c3 & 0x03) << 6) | c4) + "";     }     return out;}public static String ucAuthcode(String str, String operation, String key) throws UnsupportedEncodingException {return ucAuthcode(str, operation, key, 0);}public static String ucAuthcode(String str, String operation) throws UnsupportedEncodingException {return ucAuthcode(str, operation, "", 0);}}




java版(小范围内测通过)
0 0
原创粉丝点击