Проблема, с которой вы столкнулись, связана с кодировкой различий.Существует несколько причин, по которым кодирование одной и той же строки может давать разные результаты:
- разные окончания строк (CR / LF для Windows, LF для Linux, CR для классической MacOS);
- другие различия в пробелах (табуляция или пробелы, пробелы в концах строк);
- различные кодировки символов (Windows-1252, UTF-8 и UTF-16 или внутреннее представление символов в языковых реализациях);
- наличие метаинформации (наличие метки порядка байтов);
- различные способы обработки специальных символов в кодировке (символ, за которым следует комбинация тильды и символа с объединениемтильда, см. Эквивалентность Unicode );
Также возможны невидимые ошибки, которые могут привести к различным результатам:
- наличие непечатаемых символов / элемента управлениякоды (нулевое значение,
0x00
, в конце строки, вероятно, лучший пример);
Кроме всехИз-за различий, которые могут присутствовать для любого (структурированного) текста, структуры данных JSON также могут иметь эквивалентные значения.Вероятно, лучшим примером является ведущий символ +
перед числом.Это полностью ложно, но все равно приведет к другому текстовому представлению, но к идентичной величине числа.
Если кодировка строки отличается, тогда двоичный ввод алгоритма хеширования будет другим, и вы будетеполучить результаты, которые отличаются примерно на 50% битов для обычного криптографического хэша.Способ создания одного и того же ввода называется канонизация (или C14N, поскольку между C и N канонизации имеется 14 символов).
Для XML каноническая форма был определен давно.Для JSON это не так, хотя канонизация JSON будет намного проще.В конце концов, у JSON гораздо менее запутанный набор правил.Есть попытки канонизировать JSON, см., Например, этот проект RFC явно упоминает криптографические хеши:
Например, когда криптографический хеш применяется к документу JSON, одно физическое представление позволяетхеш для представления логического содержимого документа путем удаления изменений в том, как этот контент кодируется в JSON.
Этот черновой вариант RFC , кстати, выглядит немного более тщательно.
Пока вы можете использовать один из черновиков RFC. Если вы хотите сохранить переводы строки, вы можете сериализовать JSON, используя эти четко определенные правила, и использовать его в качестве входных данных.к хэш-функции, сохраняя при этом сам JSON нетронутым.Таким образом, по-разному отформатированный JSON все равно будет генерировать тот же хэш.
[Input JSON] -> (parse) -> (canonicalize & serialize) -> (hash) -> [hash value]
[Input JSON'] -> (parse) -> (canonicalize & serialize) -> (hash) -> [hash value']
Здесь выходные данные хеша будут идентичны, если Input JSON
и Input JSON'
структурно / семантически одинаковыпоскольку канонизация сгладит различия.
Обратите внимание, что сторона JSON Web Signatures (JWS) решает эту проблему.В конце концов, подписи используют внутренний хеш.Подпись находится над включенной полезной нагрузкой, и кодирование этой полезной нагрузки просто используется.Это нормально, если промежуточная система не перекодирует JSON.Подписи не обязательно должны быть идентичными, они просто должны проверять данные.
К сожалению, это не относится к хэшам.Однако на практике вы можете определить JSON как файл и использовать те же аргументы.Недостатком является, конечно, то, что , если вы получите разницу, вам придется выполнить двоичное сравнение, чтобы найти различия, а затем проследить, где было внесено изменение.Рабочие системы могут нарушать хеш, в то время как семантика остается прежней (например, при замене или обновлении библиотеки JSON).