如何加密哈希JSON对象?

Jas*_*ols 51 json cryptography canonicalization

以下问题比最初看起来更复杂.

假设我有一个任意的JSON对象,可能包含任何数量的数据,包括其他嵌套的JSON对象.我想要的是JSON数据的加密哈希/摘要,而不考虑实际的JSON格式本身(例如:忽略换行符和JSON令牌之间的间距差异).

最后一部分是一项要求,因为JSON将由许多不同平台上的各种(de)序列化程序生成/读取.我知道至少有一个Java的JSON库在反序列化期间读取数据时完全删除了格式.因此,它将打破哈希.

上面的任意数据子句也使事情变得复杂,因为它阻止我以给定顺序获取已知字段并在哈希之前连接它们(大致考虑Java的非加密hashCode()方法是如何工作的).

最后,将整个JSON字符串散列为一块字节(在反序列化之前)也是不可取的,因为在计算散列时应该忽略JSON中的字段.

我不确定这个问题有一个很好的解决方案,但我欢迎任何方法或想法=)

Che*_*eso 44

当计算任何允许灵活性的数据格式的哈希时,问题是常见的问题.要解决此问题,您需要规范化表示.

例如,Twitter和其他服务用于身份验证的OAuth1.0a协议需要请求消息的安全散列.要计算哈希值,OAuth1.0a表示您需要首先按字母顺序排列字段,按换行符分隔它们,删除字段名称(众所周知),并使用空行表示空值.签名或散列是根据规范化的结果计算的.

XML DSIG以相同的方式工作 - 您需要在签名之前规范化XML.有一个提议的W3标准涵盖了这一点,因为它是签署的基本要求.有人称之为c14n.

我不知道json的规范化标准.值得研究.

如果没有,您当然可以为您的特定应用程序使用建立约定.合理的开端可能是:

  • 按字典顺序按属性对属性进行排序
  • 所有名称上使用的双引号
  • 所有字符串值使用的双引号
  • 名称和冒号之间以及冒号和值之间没有空格或一个空格
  • 值与下面的逗号之间没有空格
  • 所有其他空白区域都折叠到一个空格或者没有任何东西 - 选择一个
  • 排除您不想签名的任何属性(例如,包含签名本身的属性)
  • 使用您选择的算法对结果进行签名

您可能还想考虑如何在JSON对象中传递该签名 - 可能会建立一个众所周知的属性名称,如"nichols-hmac"或其他东西,它获取哈希的base64编码版本.必须通过散列算法明确排除此属性.然后,JSON的任何接收器都能够检查散列.

规范化表示不需要是您在应用程序中传递的表示.只需要在给定任意JSON对象的情况下轻松生成它.

  • 规范化还必须考虑到人物的表现形式:`"A"`vs`"\ u0041"`,`"é"`vs`"\ u00e9"`vs`"\ u00E9"`.数字相同的问题:`1` vs`0.1e1`. (4认同)

Nik*_*kin 6

您可能希望使用bencode而不是发明自己的JSON规范化/规范化.在语义上它与JSON(数字,字符串,列表和dicts的组合)相同,但具有加密散列所必需的明确编码的属性.

bencode用作torrent文件格式,每个bittorrent客户端都包含一个实现.

  • 我的意思是在散列之前仅使用bencode作为规范化步骤.在您的哈希例程之外,一切都保持JSON. (4认同)
  • [`bencoding`](http://www.bittorrent.org/beps/bep_0003.html#bencoding)仅编码字节字符串,而JSON编码Unicode字符串.所以你必须在bencode之上设计一个JSON字符串规范化.并且bencode不编码JSON具有的浮点值. (2认同)

Ben*_*Ben 5

这与导致S / MIME签名和XML签名出现问题的问题相同。即,要签名的数据有多个等效表示。

例如在JSON中:

{  "Name1": "Value1", "Name2": "Value2" }
Run Code Online (Sandbox Code Playgroud)

{
    "Name1": "Value\u0031",
    "Name2": "Value\u0032"
}
Run Code Online (Sandbox Code Playgroud)

或者根据您的应用程序,这甚至可能是等效的:

{
    "Name1": "Value\u0031",
    "Name2": "Value\u0032",
    "Optional": null
}
Run Code Online (Sandbox Code Playgroud)

规范化可以解决该问题,但这是您根本不需要的问题。

如果您可以控制规范,则简单的解决方案是将对象包装在某种容器中,以防止将其转换为“等效”但不同的表示形式。

即通过不对“逻辑”对象签名而不是对它的特定序列化表示形式签名来避免该问题。

例如,JSON对象-> UTF-8文本->字节。将字节标记为字节,然后将其作为字节传输例如通过base64编码。由于您要对字节进行签名,因此诸如空格之类的差异是已签名内容的一部分。

而不是尝试这样做:

{  
   "JSONContent": {  "Name1": "Value1", "Name2": "Value2" },
   "Signature": "asdflkajsdrliuejadceaageaetge="
}
Run Code Online (Sandbox Code Playgroud)

只是这样做:

{
   "Base64JSONContent": "eyAgIk5hbWUxIjogIlZhbHVlMSIsICJOYW1lMiI6ICJWYWx1ZTIiIH0s",
   "Signature": "asdflkajsdrliuejadceaageaetge="

}
Run Code Online (Sandbox Code Playgroud)

即,不要对JSON签名,请对已编码 JSON 的字节签名。

是的,这意味着签名不再透明。

  • 自从我专注于这个问题已经好几年了,但是如果我今天必须实现哈希,这就是我要采用的方法。 (2认同)