Как криптографически хэшировать объект JSON?
следующий вопрос более сложный, чем может показаться на первый взгляд.
предположим, что у меня есть произвольный объект JSON, который может содержать любой объем данных, включая другие вложенные объекты JSON. Я хочу криптографический хэш / дайджест данных JSON, независимо от фактического форматирования JSON (например: игнорирование новых строк и различий между токенами JSON).
последняя часть является требованием, так как JSON будет сгенерирован / прочитан разнообразие (de)сериализаторов на нескольких различных платформах. Я знаю по крайней мере одну библиотеку JSON для Java, которая полностью удаляет форматирование при чтении данных во время десериализации. Таким образом, он сломает хэш.
произвольные пункте данных и усложняет, так как он предотвращает меня от известных месторождений, расположенных в определенном порядке объединить их до hasing (думаю, примерно как в Java не криптографический хэш-код() метод работает).
наконец, хэширования вся строка JSON в виде куска байтов (до десериализации) также нежелательна, поскольку в JSON есть поля, которые следует игнорировать при вычислении хэша.
Я не уверен, что есть хорошее решение этой проблемы, но я приветствую любые подходы или мысли =)
6 ответов
проблема является распространенной при вычислении хэшей для любого формата данных, где допускается гибкость. Чтобы решить эту проблему, вам нужно канонизируют представление.
например, OAuth1.Протокол 0a, который используется Twitter и другими службами для аутентификации, требует безопасного хэша сообщения запроса. Для вычисления хэша, OAuth1.0а говорит, что нужно сначала по алфавиту поля, разделить их на строки, удалить имена полей (которые хорошо known) и использовать пустые строки для пустых значений. Подпись или хэш вычисляется в результате этой канонизации.
XML DSIG работает так же - вам нужно канонизировать XML перед подписанием. Есть предлагаемый стандарт W3, охватывающий это, потому что это такое фундаментальное требование для подписания. Некоторые называют его c14n.
Я не знаю стандарта канонизации для json. Это стоит исследовать.
Если нет во-первых, вы можете установить Соглашение для вашего конкретного использования приложения. Разумным началом может быть:
- лексикографически сортировать свойства по имени
- двойные кавычки, используемые на всех именах
- двойные кавычки, используемые для всех строковых значений
- нет пробелов или одно пробел, между именами и двоеточием, а также между двоеточием и значением
- нет пробелов между значениями и следующей запятой
- все другое белое пространство рухнуло либо в одно пространство , либо в ничто-выберите один
- исключить любые свойства, которые вы не хотите подписывать (например, свойство, которое содержит саму подпись)
- подпишите результат, с выбранным алгоритмом
вы также можете подумать о том, как передать эту подпись в объекте JSON-возможно, установить известное имя свойства, например "Николс-hmac" или что-то еще, что получает кодированную версию base64 гашиш. Это свойство должно быть явно исключено алгоритмом хэширования. Тогда любой получатель JSON сможет проверить хэш.
канонизированное представление не должно быть представлением, которое вы передаете в приложении. Его нужно только легко произвести, учитывая произвольный объект JSON.
вместо изобретения собственной нормализации / канонизации JSON вы можете использовать bencode. Семантически это то же самое, что и JSON (состав чисел, строк, списков и диктов), но со свойством однозначного кодирования, которое необходимо для криптографического хеширования.
bencode используется как формат торрент-файла, каждый клиент bittorrent содержит реализацию.
Это та же проблема, что и проблемы с сигнатурами S/MIME и XML-сигнатурами. То есть существует несколько эквивалентных представлений подписываемых данных.
например в JSON:
{ "Name1": "Value1", "Name2": "Value2" }
и
{
"Name1": "Value\u0031",
"Name2": "Value\u0032"
}
или в зависимости от вашего приложения, это может даже быть эквивалентны:
{
"Name1": "Value\u0031",
"Name2": "Value\u0032",
"Optional": null
}
канонизация может решить эту проблему, но это проблема, которая вам вообще не нужна.
самое простое решение, если у вас есть управление спецификацией заключается в том, чтобы обернуть объект в какой-то контейнер, чтобы защитить его от преобразования в "эквивалентное", но другое представление.
т. е. избегайте проблемы, не подписывая "логический" объект, а подписывая вместо него определенное сериализованное представление.
например, объекты JSON - > UTF - 8 Text - > Bytes. Знак байт как байт, а затем передать их как байт например, по кодировке base64. С вы подписываете байты, различия, такие как пробелы, являются частью того, что подписано.
вместо того, чтобы пытаться сделать это:
{
"JSONContent": { "Name1": "Value1", "Name2": "Value2" },
"Signature": "asdflkajsdrliuejadceaageaetge="
}
просто сделать это:
{
"Base64JSONContent": "eyAgIk5hbWUxIjogIlZhbHVlMSIsICJOYW1lMiI6ICJWYWx1ZTIiIH0s",
"Signature": "asdflkajsdrliuejadceaageaetge="
}
т. е. не подписывайте JSON, подписывайте байты кодируются в формате JSON.
Да, это означает, что подпись не прозрачный.
JSON-LD могу сделать normalitzation.
вам нужно будет определить свой контекст.
Я бы сделал все поля в заданном порядке (в алфавитном порядке, например). Почему произвольные данные имеют значение? Вы можете просто перебирать свойства (отражение ala).
альтернативно, я бы посмотрел на преобразование необработанной строки json в некоторую четко определенную каноническую форму (удалите все сверхтонкое форматирование) - и хэширование этого.
мы столкнулись с простой проблемой с хэшированием полезных нагрузок, закодированных в JSON. В нашем случае мы используем следующую методику:
- преобразование данных в объект JSON;
- кодировать полезную нагрузку JSON в base64
- дайджест сообщений (HMAC) сгенерированная полезная нагрузка base64 .
- передайте полезную нагрузку base64 .
преимущества использования этого решения:
- Base64 произведет такой же выход для данного полезная нагрузка.
- поскольку результирующая подпись будет получена непосредственно из полезной нагрузки, закодированной в base64, и поскольку полезная нагрузка base64 будет обмениваться между конечными точками, мы будем уверены, что подпись и полезная нагрузка будут поддерживаться.
- Это решение решает проблемы, которые возникают из-за разницы в кодировке специальных символов.
недостатки
- кодирование/декодирование полезной нагрузки может добавить накладные
- данные, закодированные в Base64, обычно на 15-20% больше исходной полезной нагрузки.