如何从JSON获取相同的哈希

时间:2019-03-20 08:26:28

标签: json go hash cryptography

我需要对JSON进行签名,但我发现解组/编组可以更改JSON的顺序,这可能会使签名无效。

不管顺序如何,是否仍然可以从JSON字符串中产生相同的哈希值?

我看过JOSE,但找不到真正散列JSON的函数。

1 个答案:

答案 0 :(得分:0)

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))
}

该程序的输出(至少在golang docker容器中)为:

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

这种方法的好处是,为了提高性能,您首先将JSON编组为JSON时,就可以避免做任何笨拙的垃圾(因此,与其他建议不同,您不必考虑关于您可能会使用自定义编组器做什么以及其他操作)。当您忘记从一年以后的代码版本3.8中根本就没有这个问题,实施与元帅命令混乱的事情并开始破坏事情时,这尤其重要。

当然,您始终可以将哈希添加到生成的结构中,并使用地图中的多余项再次编组。显然,如果您完全担心并可以正确处理错误,则希望对性能进行一些优化,但这仍然是一个很好的原型:-)

哦,如果您担心边缘情况会咬伤您,您还可以使用canonical JSON进行编组,因为它是专门为这种类型的使用而设计的(不过,老实说,我不能在我的测试中举了一个例子,其中c-json起作用,但是go的默认json没有起作用。