根据这个问题的相关答案,我试图整理一个类似于这个PHP进程的打包/解包解决方案,但是在Nodejs(Javascript)中使用md5和bufferpack
这是PHP方法(改编自DaloRADIUS:
$challenge = 'c731395aca5dcf45446c0ae83db5319e'; $uamsecret = 'secret'; $password = 'password'; $hexchal = pack ("H32", $challenge); $newchal = pack ("H*", md5($hexchal . $uamsecret)); $response = md5("\0" . $password . $newchal); $newpwd = pack("a32", $password); $pappassword = implode ("", unpack("H32", ($newpwd ^ $newchal))); echo "Response: ---> ", $response, "\n"; echo "New Password: ---> ", $newpwd, "\n"; echo "Pap Password: ---> ", $pappassword, "\n";
以上回声如下:
以明文为准:
Response: ---> 2d4bd27184f5eb032641137f728c6043 New Password: ---> password Pap Password: ---> 356a1fb08f909fc400dfe448fc483ce3
在Javascript中,这是我现在正在做的事情:
var challenge = 'c731395aca5dcf45446c0ae83db5319e'; var uamsecret = 'secret'; var password = 'password'; var hexchal = pack.pack("H32", challenge); var newchal = pack.pack("H*", md5(hexchal + uamsecret)); var response = md5("\0" + password + newchal); var newpwd = pack.pack("a32", password); var pappassword = pack.unpack("H32", (newpwd ^ newchal)).join(""); console.log("Response: --> ", response); console.log("New Password: -->", newpwd); console.log("Pap Password: --->", pappassword);
结果如下:
在JSON中:
在明文中:
Response: --> e8a54a55cbcd81dbc2bdfd9b197d62af New Password: -->Pap Password: ---> NaN
以上所有代码段均可在此处获得:RadiusNES
我在整个过程中的理解并不是最好的,并且会欣赏见解以及我出错的地方.
为什么会出现不匹配?
转换不起作用,因为PHP Pack函数使用不同的格式字符串并返回字符串,而Javascript bufferpack模块返回数组.你也不能在Javascript中xor字符串.
虽然可能有模块可以执行您想要的操作,但我有自己的函数来解析十六进制字符串.我也喜欢修改并非每个人都同意的原型,但这些原型可以转换为常规功能.
String.prototype.pad = function( length ,padding ) { var padding = typeof padding === 'string' && padding.length > 0 ? padding[0] : '\x00' ,length = isNaN( length ) ? 0 : ~~length; return this.length < length ? this + Array( length - this.length + 1 ).join( padding ) : this; } String.prototype.packHex = function() { var source = this.length % 2 ? this + '0' : this ,result = ''; for( var i = 0; i < source.length; i = i + 2 ) { result += String.fromCharCode( parseInt( source.substr( i , 2 ) ,16 ) ); } return result; } var challenge = 'c731395aca5dcf45446c0ae83db5319e' ,uamsecret = 'secret' ,password = 'password'; var hexchal = challenge.packHex(); var newchal = md5( hexchal + uamsecret ).packHex(); var response = md5( '\0' + password + newchal ); var newpwd = password.pad( 32 ); var pappassword = ''; for( var i = 0; i < newchal.length; i++ ) { pappassword += ( newpwd.charCodeAt( i ) ^ newchal.charCodeAt( i ) ).toString( 16 ); } console.log("Response: --> ", response); console.log("New Password: -->", newpwd); console.log("Pap Password: --->", pappassword);
在String原型中定义了两个函数来替换pack
函数的使用:
.pad( 32, string )
用于填充带有空值的字符串以提供与之相同的结果pack( 'a32', string )
.虽然这里不需要它,但是如果想要用除空值之外的字符填充字符串,它还需要第二个参数.
.packHex
是将pack( 'H*' ,string )
每对十六进制字符的代码等效并转换为字符.理想情况下,该函数需要更多验证来测试字符串是否为有效的十六进制值.
在定义输入之后,接下来的四行代替使用这些函数设置变量而不是pack
.
因为Javascript本身不能xor字符串,所以你需要使用循环来提取每个字符,将其转换为数字,xor这些值,然后将结果转换回字符以创建pappassword字符串.
对我来说,这将会回归:
Response: --> – "fbfd42ffde05fcf8dbdd02b7e8ae2d90" New Password: --> – "password????????????????????????" Pap Password: ---> – "dcbdacb03f5d38ca33c128b931c272a"
这是一个结果,但不幸的是与PHP代码不同.
这是因为我的PHP安装配置为在内部使用ISO-8859-1编码,而Javascript本身使用UTF-16.
这在正常使用中不是问题,但这意味着各个md5
函数将看到不同的值,因此返回不同的散列.
假设您正在使用PHP后端编写身份验证例程,您显然需要一致的结果.可能有一些模块可用于转换Javscript值的编码以实现兼容性,但更容易更改PHP代码.
因为我们知道十六进制字符串将是一个字节,Javascript实际上是使用UTF-8,所以PHP可以通过使用该utf8_encode()
函数在md5ing之前转换打包的十六进制字符串来做同样的事情.
最初我认为Javascript在内部将编码的十六进制字符转换为它们的unicode等价物,因为这样,但事实并非如此.相反,它是在Javascript中使用的md5模块,它在输入上执行UTF-8转换.
这留下了两种可能的选择.
1.使用UTF-8 PHP
如果可能,您可以重新配置PHP服务器以使用UTF-8编码.或者您可以更改脚本以使用该utf8_encode()
函数镜像Javascript中发生的相同过程,并将十六进制打包字符串转换为UTF-8,然后再将其传递给md5()
$challenge = 'c731395aca5dcf45446c0ae83db5319e'; $uamsecret = 'secret'; $password = 'password'; $hexchal = pack ("H32", $challenge); $newchal = pack ("H*", md5(utf8_encode($hexchal) . $uamsecret)); $response = md5("\0" . $password . utf8_encode($newchal)); $newpwd = pack("a32", $password); $pappassword = implode ("", unpack("H32", ($newpwd ^ $newchal))); echo "Response: ---> ", $response, "\n"; echo "New Password: ---> ", $newpwd, "\n"; echo "Pap Password: ---> ", $pappassword, "\n";
然后返回与Javscript相同的结果:
Response: ---> fbfd42ffde05fcf8dbdd02b7e8ae2d90 New Password: ---> password Pap Password: ---> dcbdacb03f5d38ca33c128b9310c272a
2.在Javascript中更改md5模块
我假设您正在使用bluimp JavaScript-MD5模块,因为这是您链接的DaloRADIUS例程所使用的.您可以对其进行修补以绕过UTF-8转换.
有多种方法可以修补它,但第259行是md5()
函数本身的定义.这只是一组if
语句来解析输入选项并调用适当的内部函数.
但是,此块调用的函数只是通过str2rstrUTF8()
之前调用的函数提供UTF-8输入转换,然后调用相应的函数来提供实际的散列.因此,您可能希望修补md5()
接受第三个参数以指示是否应该应用UTF-8转换,然后根据需要调用其他函数.
但是,要简单地完全删除转换,更简单的方法是更改str2rstrUTF8()
以返回输入不变.此功能可以在第239行找到,将其更改为只读如下将停止转换:
function str2rstrUTF8 (input) { return input }
或者,要删除冗余函数调用,您只需删除对它的引用即可.更改从第246行开始的功能,如下所示:
function rawMD5 (s) { return rstrMD5(s) }
第rawHMACMD5()
252行的功能还包括对str2rstrUTF8()
函数的调用,您可能还需要对其进行修补以保持一致性,但上述例程不需要这样.当传递第二个参数以提供密钥散列时,调用该函数,这是本机PHP md5()
函数中不可用的特性.
在进行这些更改之后,Javascript例程现在返回与原始(ISO-8859-1使用)PHP代码相同的输出:
Response: --> – "2d4bd27184f5eb032641137f728c6043" New Password: --> – "password????????????????????????" Pap Password: ---> – "356a1fb08f909fc40dfe448fc483ce3"