Как криптографически хешировать объект JSON? - PullRequest
49 голосов
/ 12 января 2011

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

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

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

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

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

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

Ответы [ 7 ]

43 голосов
/ 12 января 2011

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

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

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

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

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

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

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

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

5 голосов
/ 12 января 2011

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

В качестве торрент-файла используется bencode, каждый битторрент-клиент содержит реализацию.

4 голосов
/ 06 декабря 2016

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

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

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

против

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

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

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

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

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

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

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

Вместо попытки сделать это:

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

Просто сделайте это:

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

}

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

Да, это означает, что подпись больше не прозрачна.

3 голосов
/ 31 января 2015

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

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

1 голос
/ 22 декабря 2018

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

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

0 голосов
/ 05 апреля 2018

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

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

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

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

Недостатки

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

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

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

...