Согласно ответу на этот вопрос , я пытаюсь собрать решение pack / unpack, похожее на этот процесс 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: --> <Buffer > Pap Password: ---> NaN
Все приведенные ниже фрагменты доступны здесь: RadiusNES
Мое понимание во всем этом процессе не самое лучшее, и я ценю идеи и где я ошибаюсь.
Почему существует несоответствие?
Перевод не работает, потому что функция PHP Pack использует разные строки форматирования и возвращает строки, тогда как модуль bufferpack Javascript возвращает массивы. Также вы не можете использовать xor строки в Javascript.
Хотя могут быть модули, которые вы хотите делать, у меня есть свои собственные функции для разбора шестнадцатеричных строк. Также мне нравится модифицировать прототипы, с которыми не все согласны, но они могут быть преобразованы в обычные функции.
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 )
используется для .pad( 32, string )
строки с нулями, чтобы получить те же результаты, что и pack( 'a32', string )
. Хотя это и не нужно здесь, он также принимает второй параметр, если требуется заполнить строку символом, отличным от NULL.
.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. Измените модуль md5 в Javascript
Я предполагаю, что вы используете модуль bluimp JavaScript-MD5 , так как это то, что он использовал в рутине DaloRADIUS, которую вы связали. Вы можете исправить это, чтобы обойти конвертирование UTF-8.
Существуют различные способы исправления этого, но в строке 259 это определение самой функции md5()
. Это просто набор операторов if
для анализа параметров ввода и вызова соответствующей внутренней функции.
Однако функции, вызываемые этим блоком, просто обеспечивают входное преобразование UTF-8 через функцию str2rstrUTF8()
а затем вызывают соответствующие функции для обеспечения фактического хэширования. Поэтому вы можете исправить 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"