Как получить такой же хеш из JSON - PullRequest
5 голосов
/ 20 марта 2019

Мне нужно подписать JSON, но я заметил, что демаршалинг / маршалинг может изменить порядок JSON, что может сделать подпись недействительной.

В любом случае, можно ли создать такой же хеш из строки JSON, несмотря на ее порядок?

Я посмотрел на JOSE, но не смог найти функцию, которая на самом деле хэширует JSON.

1 Ответ

0 голосов
/ 11 апреля 2019

JOSE JWS будет делать то, что вы хотите, за счет управления ключами для подписей и проверки.

Но давайте предположим, что вам на самом деле не нужны все средства управления ключами и общие функции шифрования в JOSE, и вы не СУПЕР заботитесь о производительности (поэтому небольшое искажение строк в этом процессе нормально).

Вы можете тупо разархивировать свой JSON и повторно его маршалировать, а затем просто хешировать это:

package main

import (
    "crypto/sha256"
    "encoding/hex"
    "fmt"
    json "encoding/json"
)

// NB These docs are strictly-speaking the same.
const DOCA = "{ \"foo\": 1.23e1, \"bar\": { \"baz\": true, \"abc\": 12 } }"
const DOCB = "{ \"bar\": { \"abc\": 12, \"baz\": true }, \"foo\": 12.3 }"

func hash(doc string) string {
    // Dumb af, but it's a cheap way to specific the most generic thing
    // you can :-/
    var v interface{}
    json.Unmarshal([]byte(doc), &v) // NB: You should handle errors :-/
    cdoc, _ := json.Marshal(v)
    sum := sha256.Sum256(cdoc)
    return hex.EncodeToString(sum[0:])
}

func main() {
    fmt.Println(DOCA)
    fmt.Printf("Hash: %s\n", hash(DOCA))
    fmt.Println(DOCB)
    fmt.Printf("Hash: %s\n", hash(DOCB))
}

Вывод этой программы (по крайней мере, в Docker-контейнере Golang):

{ "foo": 1.23e1, "bar": { "baz": true, "abc": 12 } }
Hash: d50756fbb830f8335187a3f427603944c566772365d8d8e6f6760cd2868c8a73
{ "bar": { "abc": 12, "baz": true }, "foo": 12.3 }
Hash: d50756fbb830f8335187a3f427603944c566772365d8d8e6f6760cd2868c8a73

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

И, конечно, вы всегда можете добавить хеш к полученной структуре и снова выполнить маршал с дополнительным элементом на карте. Очевидно, что вы хотите немного оптимизировать производительность, если вас это вообще беспокоит и правильно обрабатывает ошибки, но в любом случае это хороший прототип: -)

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

...