如何对JSON对象进行加密哈希?

snz8szmq  于 2023-05-19  发布在  其他
关注(0)|答案(9)|浏览(233)

下面的问题比乍看起来要复杂得多。
假设我有一个任意的JSON对象,它可以包含任何数量的数据,包括其他嵌套的JSON对象。我想要的是JSON数据的加密哈希/摘要,而不考虑实际的JSON格式本身(例如:忽略JSON标记之间的换行符和间距差异)。
最后一部分是一个需求,因为JSON将由许多不同平台上的各种序列化器(反序列化器)生成/读取。我知道至少有一个Java的JSON库在反序列化过程中阅读数据时完全删除了格式。因此,它将打破散列。
上面的任意数据子句也使事情变得复杂,因为它阻止了我以给定的顺序获取已知字段并在hasing之前将它们连接起来(大致想想Java的非加密hashCode()方法是如何工作的)。
最后,将整个JSON字符串散列为一个字节块(在反序列化之前)也是不可取的,因为在计算散列时应该忽略JSON中的字段。
我不确定有一个很好的解决这个问题,但我欢迎任何方法或想法=)

os8fio9y

os8fio9y1#

当计算任何允许灵活性的数据格式的散列时,这个问题是常见的。为了解决这个问题,你需要 * 规范化 * 表示。
例如,Twitter和其他服务用于身份验证的OAuth1.0a协议需要请求消息的安全散列。为了计算哈希值,OAuth1.0a要求您首先按字母顺序排列字段,用换行符分隔它们,删除字段名称(众所周知),并使用空行表示空值。签名或散列是根据该规范化的结果计算的。
XMLDSIG的工作方式相同-您需要在签名之前规范化XML。有一个proposed W3 standard covering this,因为它是签名的基本要求。有些人称之为C14 N。
我不知道json的规范化标准。值得研究。
如果没有,您当然可以为您的特定应用程序用法建立一个约定。一个合理的开始可能是:

  • 按名称对属性进行字典式排序
  • 双引号用于所有名称
  • 双引号用于所有字符串值
  • 名称和冒号之间以及冒号和值之间没有空格或只有一个空格
  • 值和后面的逗号之间没有空格
  • 所有其他白色折叠为单个空格或空-选择一个
  • 排除您不想签名的任何属性(例如,包含签名本身的属性)
  • 用你选择的算法在结果上签名

您可能还想考虑如何在JSON对象中传递该签名-可能建立一个众所周知的属性名称,如“nichols-hmac”或其他名称,以获取base64编码版本的散列。哈希算法必须明确排除此属性。然后,JSON的任何接收者都可以检查哈希。
规范化的表示不需要是您在应用程序中传递的表示。它只需要在给定任意JSON对象的情况下轻松生成。

cld4siwp

cld4siwp2#

您可能希望使用bencode,而不是发明自己的JSON规范化/规范化。从语义上讲,它与JSON(数字、字符串、列表和字典的组合)相同,但具有加密哈希所需的明确编码属性。
bencode被用作torrent文件格式,每个bittorrent客户端都包含一个实现。

hrysbysz

hrysbysz3#

这与导致S/MIME签名和XML签名出现问题的问题相同。也就是说,存在待签名的数据的多个等效表示。
例如JSON:

{  "Name1": "Value1", "Name2": "Value2" }

对比

{
    "Name1": "Value\u0031",
    "Name2": "Value\u0032"
}

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

{
    "Name1": "Value\u0031",
    "Name2": "Value\u0032",
    "Optional": null
}

规范化可以解决这个问题,但你根本不需要这个问题。
如果您可以控制规范,那么简单的解决方案是将对象 Package 在某种容器中,以保护它不会被转换为“等效”但不同的表示。
也就是说,通过不签署“逻辑”对象而是签署它的特定序列化表示来避免问题。
例如,JSON对象-> UTF-8文本->字节。将字节签名为字节,然后将它们作为字节传输,例如base64编码由于您正在对字节进行签名,所以像空格这样的差异是签名内容的一部分。
而不是试图这样做:

{  
   "JSONContent": {  "Name1": "Value1", "Name2": "Value2" },
   "Signature": "asdflkajsdrliuejadceaageaetge="
}

就这样做:

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

}

也就是说,不要对JSON进行签名,而是对编码的 * JSON的字节进行 * 签名。
是的,这意味着签名不再是透明的。

uyto3xhc

uyto3xhc4#

JSON-LD可以进行归一化。
你必须定义你的上下文。

hjqgdpho

hjqgdpho5#

RFC 7638:JSON Web Key(JWK)Thumbprint包含一种规范化类型。虽然RFC7638期望有限的成员集合,但我们可以对任何成员应用相同的计算。
https://www.rfc-editor.org/rfc/rfc7638#section-3

vpfxa7rd

vpfxa7rd6#

如果JavaScript本身为JavaScript对象定义了一个正式的散列过程,那将是理想的。
然而,我们确实有RFC-8785 JSON Canonicalization Scheme (JCS),希望它可以在大多数JSON库中实现,特别是添加到流行的JavaScript JSON对象中。完成规范化后,只需应用您首选的哈希算法即可。
如果JCS在浏览器和其他工具和库中可用,那么期望大多数在线JSON都采用这种常见的规范化形式就变得合理了。像这样的标准的共同一致的应用和验证可以在某种程度上推动低技能参与者应对微不足道的安全威胁。

ecbunoof

ecbunoof7#

我会按照给定的顺序(例如按字母顺序)处理所有字段。为什么任意数据会产生差异?您可以只迭代属性(ala反射)。
或者,我会考虑将原始json字符串转换为一些定义良好的规范形式(删除所有多余的格式)并对其进行散列。

cwtwac6a

cwtwac6a8#

我们在对JSON编码的有效负载进行散列时遇到了一个简单的问题。在我们的案例中,我们使用以下方法:
1.将数据转换为JSON对象;
1.在base64中编码JSON负载
1.消息摘要(HMAC)生成的base64有效载荷。
1.发送base64有效载荷。

使用此方案的优势:

  1. Base64将为给定的有效负载产生相同的输出。
    1.由于所得到的签名将直接从base64编码的有效载荷中导出,并且由于base64有效载荷将在端点之间交换,因此我们将确保签名和有效载荷将被维护。
    1.该解决方案解决了由于特殊字符编码的差异而出现的问题。

缺点

1.有效载荷的编码/解码可能增加开销

  1. Base64编码的数据通常比原始有效负载大30+%。
ddrv8njm

ddrv8njm9#

所以我不知道为什么这里没有提到库,但你可以使用像https://www.npmjs.com/package/@tufjs/canonical-json这样的东西作为第一步,然后使用你选择的任何哈希算法。

相关问题