首页 >后端开发 >php教程 >关于DZ的Authcode函数转JS版的问题。

关于DZ的Authcode函数转JS版的问题。

WBOY
WBOY原创
2016-06-23 14:20:441401浏览

本帖最后由 TottyAndBaty 于 2013-08-30 23:43:38 编辑

之前论坛有人问起过这个问题,原文

http://bbs.csdn.net/topics/390310377?page=1#post-393233055


我尝试把这个php版本的authcode写成了js版本的,但是结果相差太远。

PHP中对应JS的一些函数可以在这里找到:
chr: http://phpjs.org/functions/chr/

ord: http://phpjs.org/functions/ord/

Base64.encode,Base64.decode  http://www.webtoolkit.info/javascript-base64.html

md5: http://phpjs.org/functions/md5/

其中Base64.encode和Base64.decode 测试结果与php的一样、。


那个帖子中, 版主说“由于涉及字符集问题(js 始终使用unicode),直译后与php不对等,没有大大意义”。

DZ的Authcode函数中用了RC4算法,

  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]));      }  


这部分如果写成JS版本,那么问题就和php版的不一样了。$string 在这段代码还没运行之前是一样的,但是运行过后就对不上了。

附代码:

function 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用于变化生成的密文	var keyc = ckey_length ? (operation == 'DECODE' ? str.substr(0, ckey_length): md5(microtime()).substr(-ckey_length)) : '';		// 参与运算的密匙	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 = '';	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 += String.fromCharCode(string[i] ^ (box[(box[a] + box[j]) % 256]));	    strbuf[i] =chr(ord(strbuf[i]) ^ (box[(box[a] + box[j]) % 256]))	}	if(operation == 'DECODE') {		var s = strbuf.toString();		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 {		var s = Base64.encode(strbuf.toString());		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;}


php 版:

   // 参数解释  // $string: 明文 或 密文  // $operation:DECODE表示解密,其它表示加密  // $key: 密匙  // $expiry:密文有效期  function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {      // 动态密匙长度,相同的明文会生成不同密文就是依靠动态密匙      $ckey_length = 4;            // 密匙      $key = md5($key ? $key : $GLOBALS['discuz_auth_key']);            // 密匙a会参与加解密      $keya = md5(substr($key, 0, 16));      // 密匙b会用来做数据完整性验证      $keyb = md5(substr($key, 16, 16));      // 密匙c用于变化生成的密文      $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';      // 参与运算的密匙      $cryptkey = $keya.md5($keya.$keyc);      $key_length = strlen($cryptkey);      // 明文,前10位用来保存时间戳,解密时验证数据有效性,10到26位用来保存$keyb(密匙b),解密时会通过这个密匙验证数据完整性      // 如果是解码的话,会从第$ckey_length位开始,因为密文前$ckey_length位保存 动态密匙,以保证解密正确      $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') {          // substr($result, 0, 10) == 0 验证数据有效性          // substr($result, 0, 10) - time() > 0 验证数据有效性          // substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16) 验证数据完整性          // 验证数据有效性,请看未加密明文的格式          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 {          // 把动态密匙保存在密文里,这也是为什么同样的明文,生产不同密文后能解密的原因          // 因为加密后的密文可能是一些特殊字符,复制过程可能会丢失,所以用base64编码          return $keyc.str_replace('=', '', base64_encode($result));      }  }  

回复讨论(解决方案)

js 代码部分
68  strbuf[i] =chr(ord(strbuf[i]) ^ (box[(box[a] + box[j]) % 256]))
这个 strbuf 是数组吗?
无论从
21 strbuf = Base64.decode(str);
还是
36 strbuf = str;
上看,strbuf 都是字符串
那么 strbuf[i] = 'x' 这样写是无效的,虽然不报错
同样 ord(strbuf[i]) 也是不能返回正确值的

对应 ord(strbuf[i]) 的 js 是
strbuf.charCodeAt(i)

对应 chr(n) 的 js 是
String.fromCharCode(n)

没有认真去看你提供的php同名函数,但至少你的 js 取值、赋值部分已经就出问题了

另外:
DZ 的发行版是分 utf-8 和 gbk 的
由于字符内码的原因,utf-8 版中的 Authcode 编码结果是不能在 gbk 版中正确解码的(解出的还是utf-8的)
当然如果不含有中文是没有问题的,这一点你在测试的时候一定要注意

那个 Base64 类也是针对 utf-8 编码的。如果 php 端不是 utf-8 的,你也不能得到相同的结果

不好意思,疏忽了。发错了版本。

这段代码是我最后修正的结果:
 

function 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用于变化生成的密文	var keyc = ckey_length ? (operation == 'DECODE' ? str.substr(0, ckey_length): md5(microtime()).substr(-ckey_length)) : '';		// 参与运算的密匙	var cryptkey = keya+md5(keya+keyc);		  	var string="";	if(operation == 'DECODE') {		string =Base64.decode(str.substr(ckey_length));	}	else {	 		expiry = expiry ? expiry + time() : 0;		tmpstr = expiry.toString();		if(tmpstr.length>=10)	        string = 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;	        } 	        string = tmpstr+md5(str+keyb).substr(0, 16)+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 result = '';	for(var 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+=String.fromCharCode(string.charCodeAt(i) ^ (box[(box[a] + box[j]) % 256]));	}	 	 	if(operation == 'DECODE') {	 var s="";		if((result.substr(0, 10) == 0 || result.substr( 0, 10) - time() > 0) && result.substr(10, 16) == md5(result.substr(26)+keyb).substr(0, 16)) {		    s = result.substr(26);		}  	}	else {		var s = Base64.encode(result);		var regex = new RegExp('=', "g");		s = s.replace(regex, '');		s = keyc+s;	}	return s;}


这里基本上没采用替代版的CHR,ORD,等,都用的JS只带的。我的测试页面编码都是UTF-8编码。

这里的Base64.encode与php的base64_encode结果一样。

前文说的ord,chr 都有提供JS版本的。

chr:http://phpjs.org/functions/chr/

ord:http://phpjs.org/functions/ord/

原本放在源码的加密解密数据,你现在用JS来实现,那么问一下,密匙你准备怎么处理?直接放在JS里么?

 

原本放在源码的加密解密数据,你现在用JS来实现,那么问一下,密匙你准备怎么处理?直接放在JS里么?

傻逼客户说明文传输。。。。二逼老板慌了。。。所以要求客户端加密。。。。

 


原本放在源码的加密解密数据,你现在用JS来实现,那么问一下,密匙你准备怎么处理?直接放在JS里么?

傻逼客户说明文传输。。。。二逼老板慌了。。。所以要求客户端加密。。。。
努力攻克难关,交货后让他们被黑掉吧


 


原本放在源码的加密解密数据,你现在用JS来实现,那么问一下,密匙你准备怎么处理?直接放在JS里么?

傻逼客户说明文传输。。。。二逼老板慌了。。。所以要求客户端加密。。。。
努力攻克难关,交货后让他们被黑掉吧

唉,想通了。这样做没意义。客户端的,都是可见的。

我参考了tx的登录加密的办法,放弃这个了。。



 


原本放在源码的加密解密数据,你现在用JS来实现,那么问一下,密匙你准备怎么处理?直接放在JS里么?

傻逼客户说明文传输。。。。二逼老板慌了。。。所以要求客户端加密。。。。
努力攻克难关,交货后让他们被黑掉吧

唉,想通了。这样做没意义。客户端的,都是可见的。

我参考了tx的登录加密的办法,放弃这个了。。

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn