Подтвердить что ты не робот

Как криптографически хэш-объект JSON?

Следующий вопрос более сложный, чем может показаться на первый взгляд.

Предположим, что у меня есть произвольный объект JSON, который может содержать любое количество данных, включая другие вложенные объекты JSON. То, что я хочу, это криптографический хеш/дайджест данных JSON, независимо от собственно форматирования JSON (например: игнорирование новых строк и разность между токенами JSON).

Последняя часть является обязательным требованием, так как JSON будет генерироваться/считываться множеством (де) сериализаторов на нескольких разных платформах. Я знаю, по крайней мере, одну библиотеку JSON для Java, которая полностью удаляет форматирование при чтении данных во время десериализации. Таким образом, он сломает хэш.

Предложение произвольных данных также усложняет ситуацию, так как это мешает мне принимать известные поля в заданном порядке и конкатенировать их до хасинга (подумайте, как работает Java некриптографический метод hashCode()).

Наконец, хеширование всей строки JSON как части байтов (до десериализации) также нежелательно, так как в JSON есть поля, которые следует игнорировать при вычислении хеша.

Я не уверен, что есть хорошее решение этой проблемы, но я приветствую любые подходы или мысли =)

4b9b3361

Ответ 1

Проблема является общей при вычислении хэшей для любого формата данных, где допускается гибкость. Чтобы решить эту проблему, вам нужно канонизировать представление.

Например, протокол OAuth1.0a, который используется Twitter и другими службами для аутентификации, требует безопасного хэша сообщения запроса. Чтобы вычислить хэш, OAuth1.0a говорит, что вам нужно сначала перенести в алфавитном порядке поля, разделить их на новые строки, удалить имена полей (которые хорошо известны) и использовать пустые строки для пустых значений. Подпись или хэш вычисляется по результатам этой канонизации.

XML DSIG работает одинаково - вам нужно канонизировать XML перед его подписанием. Существует предлагаемый стандарт W3, охватывающий это, потому что это такое фундаментальное требование для подписания. Некоторые называют это c14n.

Я не знаю стандарта канонизации для json. Это стоит исследовать.

Если его нет, вы можете, конечно, установить соглашение для использования вашего конкретного приложения. Разумным началом может быть:

  • лексикографически сортировать свойства по имени
  • двойные кавычки, используемые для всех имен
  • двойные кавычки, используемые для всех строковых значений
  • пробел или одно пробел между именами и двоеточием и между двоеточием и значением
  • нет пробелов между значениями и следующей запятой
  • все остальные пробелы свернуты либо в одно пространство, либо ничего - выберите один
  • исключить любые свойства, которые вы не хотите подписывать (один пример - это свойство, которое содержит собственно подпись)
  • подпишите результат с выбранным вами алгоритмом

Вам также может понадобиться подумать о том, как передать эту подпись в объекте JSON - возможно, установить известное имя свойства, например "nichols-hmac" или что-то еще, которое получает версию хэша с кодировкой base64. Это свойство должно быть явно исключено с помощью алгоритма хеширования. Тогда любой приемник JSON сможет проверить хэш.

Канонизованное представление не обязательно должно быть представлением, которое вы передаете в приложении. Он должен быть легко создан с учетом любого объекта JSON.

Ответ 2

Вместо того, чтобы изобретать собственную нормализацию/канонизацию JSON, вы можете использовать bencode. Семантически это то же самое, что и JSON (состав чисел, строк, списков и dicts), но со свойством однозначного кодирования, которое необходимо для криптографического хеширования.

bencode используется как формат торрент файла, каждый клиент bittorrent содержит реализацию.

Ответ 3

Это та же проблема, что и проблемы с сигнатурами S/MIME и подписями XML. То есть, есть несколько эквивалентных представлений данных, которые должны быть подписаны.

Например, в JSON:

{  "Name1": "Value1", "Name2": "Value2" }

против.

{
    "Name1": "Value\u0031",
    "Name2": "Value\u0032"
}

Или в зависимости от вашего приложения это может даже быть эквивалентным:

{
    "Name1": "Value\u0031",
    "Name2": "Value\u0032",
    "Optional": null
}

Canonicalization может решить эту проблему, но это проблема, которая вам не нужна вообще.

Простое решение, если у вас есть контроль над спецификацией, заключается в том, чтобы обернуть объект в какой-то контейнер, чтобы защитить его от преобразования в "эквивалентное", но другое представление.

т.е. избегайте проблемы, не подписывая "логический" объект, но вместо этого подписывая конкретное сериализованное представление.

Например, объекты JSON → UTF-8 Text → Bytes. Подпишите байты как байты, затем передайте их как байты, например. по кодировке base64. Поскольку вы подписываете байты, различия, такие как пробельные символы, являются частью подписанного.

Вместо этого:

{  
   "JSONContent": {  "Name1": "Value1", "Name2": "Value2" },
   "Signature": "asdflkajsdrliuejadceaageaetge="
}

Просто сделайте следующее:

{
   "Base64JSONContent": "eyAgIk5hbWUxIjogIlZhbHVlMSIsICJOYW1lMiI6ICJWYWx1ZTIiIH0s",
   "Signature": "asdflkajsdrliuejadceaageaetge="

}

т.е. не подписывайте JSON, подписывайте байты кодированного JSON.

Да, это означает, что подпись уже не прозрачна.

Ответ 4

JSON-LD может выполнять нормализацию.

Вам нужно будет определить свой контекст.

Ответ 5

Я бы сделал все поля в заданном порядке (например, в алфавитном порядке). Почему произвольные данные имеют значение? Вы можете просто перебирать свойства (отражение ала).

В качестве альтернативы я бы рассмотрел преобразование исходной строки json в некоторую четко определенную каноническую форму (удалил все сверхплотное форматирование) - и хеширование этого.

Ответ 6

Мы столкнулись с простой проблемой хэширования полезных нагрузок в кодировке JSON. В нашем случае мы используем следующую методологию:

  1. Конвертировать данные в объект JSON;
  2. Кодировать полезную нагрузку JSON в base64
  3. Дайджест сообщения (HMAC) сгенерированной полезной нагрузки base64.
  4. Передача полезной нагрузки base64.

Преимущества использования этого решения:

  1. Base64 будет производить тот же вывод для данной полезной нагрузки.
  2. Поскольку результирующая подпись будет получена непосредственно из полезной нагрузки в кодировке base64, а так как полезная нагрузка base64 будет обмениваться между конечными точками, мы будем уверены, что подпись и полезная нагрузка будут сохранены.
  3. Это решение решает проблемы, возникающие из-за различий в кодировке специальных символов.

Недостатки

  1. Кодирование/декодирование полезной нагрузки может добавить издержки
  2. Данные в кодировке Base64 обычно на 30+% больше, чем исходная полезная нагрузка.

Ответ 7

RFC 7638: отпечаток веб-ключа JSON (JWK) включает тип канонизации. Хотя RFC7638 ожидает ограниченный набор членов, мы сможем применить тот же расчет для любого участника.

https://tools.ietf.org/html/rfc7638#section-3