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

tottyandbaty 2013-08-30 11:41:57
之前论坛有人问起过这个问题,原文

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

...全文
173 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
LuciferStar 2013-09-02
  • 打赏
  • 举报
回复
引用 6 楼 TottyAndBaty 的回复:
[quote=引用 5 楼 LuciferStar 的回复:] [quote=引用 4 楼 TottyAndBaty 的回复:] [quote=引用 3 楼 LuciferStar 的回复:] 原本放在源码的加密解密数据,你现在用JS来实现,那么问一下,密匙你准备怎么处理?直接放在JS里么?
傻逼客户说明文传输。。。。二逼老板慌了。。。所以要求客户端加密。。。。[/quote] 努力攻克难关,交货后让他们被黑掉吧[/quote] 唉,想通了。这样做没意义。客户端的,都是可见的。 我参考了tx的登录加密的办法,放弃这个了。。[/quote]
tottyandbaty 2013-09-02
  • 打赏
  • 举报
回复
引用 5 楼 LuciferStar 的回复:
[quote=引用 4 楼 TottyAndBaty 的回复:] [quote=引用 3 楼 LuciferStar 的回复:] 原本放在源码的加密解密数据,你现在用JS来实现,那么问一下,密匙你准备怎么处理?直接放在JS里么?
傻逼客户说明文传输。。。。二逼老板慌了。。。所以要求客户端加密。。。。[/quote] 努力攻克难关,交货后让他们被黑掉吧[/quote] 唉,想通了。这样做没意义。客户端的,都是可见的。 我参考了tx的登录加密的办法,放弃这个了。。
LuciferStar 2013-09-02
  • 打赏
  • 举报
回复
引用 4 楼 TottyAndBaty 的回复:
[quote=引用 3 楼 LuciferStar 的回复:] 原本放在源码的加密解密数据,你现在用JS来实现,那么问一下,密匙你准备怎么处理?直接放在JS里么?
傻逼客户说明文传输。。。。二逼老板慌了。。。所以要求客户端加密。。。。[/quote] 努力攻克难关,交货后让他们被黑掉吧
tottyandbaty 2013-09-01
  • 打赏
  • 举报
回复
引用 3 楼 LuciferStar 的回复:
原本放在源码的加密解密数据,你现在用JS来实现,那么问一下,密匙你准备怎么处理?直接放在JS里么?
傻逼客户说明文传输。。。。二逼老板慌了。。。所以要求客户端加密。。。。
LuciferStar 2013-09-01
  • 打赏
  • 举报
回复
原本放在源码的加密解密数据,你现在用JS来实现,那么问一下,密匙你准备怎么处理?直接放在JS里么?
tottyandbaty 2013-09-01
  • 打赏
  • 举报
回复
不好意思,疏忽了。发错了版本。 这段代码是我最后修正的结果:


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/
xuzuning 2013-08-31
  • 打赏
  • 举报
回复
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 的,你也不能得到相同的结果

21,886

社区成员

发帖
与我相关
我的任务
社区描述
从PHP安装配置,PHP入门,PHP基础到PHP应用
社区管理员
  • 基础编程社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧