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 не работал).