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

WBOY
Release: 2016-06-23 14:20:44
Original
1363 people have browsed it

本帖最后由 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]));      }  
Copy after login


这部分如果写成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;}
Copy after login


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));      }  }  
Copy after login


回复讨论(解决方案)

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;}
Copy after login


这里基本上没采用替代版的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的登录加密的办法,放弃这个了。。

source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template