Реализация PHP Pack/Unpack в несоответствии Javascript
Согласно соответствующему ответу на этот вопрос , я пытаюсь собрать решение для упаковки/распаковки, напоминающее этот процесс PHP, однако в Nodejs (Javascript) с использованием md5 и буферный пакет
Вот подход 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
Все приведенные выше фрагменты доступны здесь: Радиусы
Мое понимание всего этого процесса не самое лучшее, и я буду признателен за понимание и за то, где я ошибаюсь.
Почему существует несоответствие?
1 answers
Перевод не работает, потому что функция PHP Pack использует строки другого формата и возвращает строки, в то время как модуль Javascript bufferpack возвращает массивы. Также вы не можете удалять строки в 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);
В прототипе строки определены две функции для замены использования функции 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()
для преобразования упакованных шестнадцатеричных строк перед их обработкой md5.
Первоначально я думал, что Javascript был внутреннее преобразование закодированных шестнадцатеричных символов в их эквиваленты в юникоде из-за этого, но это было не так. Вместо этого это был модуль md5, используемый в Javascript, который выполнял преобразование UTF-8 на входе.
Это оставляет два возможных варианта.
1. Используйте PHP в формате UTF-8
Если возможно, вы можете перенастроить свой 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"