doushajian2018 2019-03-20 08:26
浏览 219

如何从JSON获取相同的哈希

I need to sign a JSON but I noticed that unmarshaling/marshaling can change JSON's order which might make the signature invalid.

Is there anyway to produce the same hash from a JSON string despite its order?

I've had a look at JOSE but couldn't find the function that actually hashes JSON.

  • 写回答

1条回答

  • duangenshi9836 2019-04-11 03:19
    关注

    JOSE JWS will absolutely do what you want at the cost of having to manage keys for signatures and verification.

    But let's assume that you don't really need the whole key management stuff and general crypto functionality in JOSE and you're not SUPER concerned about performance (so a little string mangling in this process is OK).

    You could dumbly unmarshal your JSON and re-marshal it, then just hash that:

    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
    ", hash(DOCA))
        fmt.Println(DOCB)
        fmt.Printf("Hash: %s
    ", hash(DOCB))
    }
    

    The output of this program (at least in the golang docker container) is:

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

    The nice thing about this approach is that, for the cost of some performance, you get insulated from whatever dumb junk you did while marshalling your JSON in the first place (so, unlike other suggestions, you don't have to think about what you might be doing with custom Marshallers and whatnot). This is especially a big deal when you forget that this was an issue at all in version 3.8 of your code a year from now, implement something that messes with the marshal order, and start breaking things.

    And, of course, you could always add the hash to the resulting struct and marshal again with the extra item in the map. Obviously you want to optimize a bit for performance if you're worried about it at all and properly handle errors, but this is a good prototype anyway :-)

    Oh, and if you're super-worried about edge cases biting you, you could also use canonical JSON to marshal, since it's specifically designed for this type of use (though, honestly, I couldn't come up with an example in my testing where c-json worked but go's default json didn't).

    评论

报告相同问题?

悬赏问题

  • ¥15 微信公众号自制会员卡没有收款渠道啊
  • ¥15 stable diffusion
  • ¥100 Jenkins自动化部署—悬赏100元
  • ¥15 关于#python#的问题:求帮写python代码
  • ¥20 MATLAB画图图形出现上下震荡的线条
  • ¥15 关于#windows#的问题:怎么用WIN 11系统的电脑 克隆WIN NT3.51-4.0系统的硬盘
  • ¥15 perl MISA分析p3_in脚本出错
  • ¥15 k8s部署jupyterlab,jupyterlab保存不了文件
  • ¥15 ubuntu虚拟机打包apk错误
  • ¥199 rust编程架构设计的方案 有偿