2011-01-12 40 views
44

以下問題比首先看起來更復雜。如何加密散列JSON對象?

假設我有一個任意的JSON對象,可以包含任何數量的數據,包括其他嵌套的JSON對象。我想要的是JSON數據的加密散列/摘要,而不考慮實際的JSON格式本身(例如:忽略JSON令牌之間的換行符和間距差異)。

最後一部分是一個需求,因爲JSON將由許多不同平臺上的各種(反)序列化器生成/讀取。我知道至少有一個用於Java的JSON庫,它可以在反序列化過程中讀取數據時完全刪除格式。因此它會打破散列。

上面的任意數據子句也使事情複雜化,因爲它阻止我以給定順序接收已知字段並在hasing之前將它們連接起來(大致思考Java的非加密hashCode()方法是如何工作的)。

最後,將整個JSON字符串散列爲一個字節塊(反序列化之前)也是不可取的,因爲在計算散列時應該忽略JSON中的字段。

我不知道有很好的解決了這個問題,但是我歡迎任何方式或想法=)

+3

我不禁注意到你的名字對於這個問題有多重要。 –

+1

你有沒有看過XML DSig?他們有同樣的問題,並有一個相當複雜的「規範化」規範。 – mtraut

+5

這是標準化的。請參閱JSON Web簽名(JWS)RFC草案。 http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-17 – user239558

回答

38

的問題是計算散列對於其中的靈活性是允許任何數據格式時,常見的一種。爲了解決這個問題,你需要規範化的表示。

例如,Twitter和其他服務用於身份驗證的OAuth1.0a協議需要安全散列請求消息。要計算散列,OAuth1.0a表示您需要先將字段字母化,用換行符分隔它們,刪除字段名稱(衆所周知),並使用空行作爲空值。簽名或散列是根據該規範化的結果計算的。

XML DSIG以同樣的方式 - 你需要在簽名之前規範化的XML。有一個proposed W3 standard covering this,因爲它是簽名的基本要求。有人稱之爲c14n。

我不知道JSON一個規範化的標準。這是值得研究的。

如果沒有一個,你當然可以建立您的特定應用程序使用的約定。一個合理的開始可能是:

  • 字典順序屬性的名字在所有名稱中使用
  • 上的所有字符串中使用雙引號
  • 雙引號值
  • 沒有空間,或者一個空間,名稱之間進行排序和結腸,和結腸和值
  • 值之間沒有空格和逗號之後
  • 所有其他空白崩潰到單個空間或之間什麼都沒有 - 選擇一個
  • 排除任何屬性,你不想籤(一個例子是,持有簽名本身的屬性)
  • 簽結果,與您所選擇的算法

您可能還需要考慮如何在JSON對象中傳遞該簽名 - 可能會建立一個衆所周知的屬性名稱,如「nichols-hmac」或其他,它會獲得散列的base64編碼版本。這個屬性必須被哈希算法明確排除。然後,JSON的任何接收者將能夠檢查哈希。

規範化表示形式不需要是您在應用程序中傳遞的表示形式。只需要在給定任意JSON對象的情況下輕鬆生成它。

+2

Canonicalisation還必須考慮字符的表示:''A「'vs'」\ u0041「',''é」'vs'「\ u00e9」'vs'「\ u00E9」'。數字相同的問題:'1'與'0.1e1'。 – dolmen

+1

例如https://github.com/jchris/canonical-json – opyate

+2

檢查此:https://github.com/substack/json-stable-stringify – jbaylina

0

我會做所有字段在給定的順序(按字母順序爲例)。爲什麼任意數據有所作爲?你可以迭代屬性(ala reflection)。

另外,我會考慮將原始json字符串轉換爲一些定義良好的規範形式(刪除所有superflous格式) - 並哈希。

5

您可能希望使用bencode,而不是發明自己的JSON規範化/規範化。在語義上它與JSON(數字,字符串,列表和字典的組合)相同,但具有密碼散列所必需的明確編碼的屬性。

bencode被用作torrent文件格式,每個bittorrent客戶端都包含一個實現。

+0

JSON是非常受歡迎的,因爲幾乎所有語言都有可用於執行對象(反)序列化的庫。 –

+4

我的意思是隻使用bencode作爲哈希之前的規範化步驟。在哈希程序之外,所有東西都保持爲JSON。 –

+0

bencode非常好,超級容易實現。 Canonical JSON不會使用標準的JSON解析器進行解析。對於只需要散列函數輸入的應用程序,都不需要進行解析。 – joeforker

2

JSON-LD可以做normalization。

您將不得不定義您的上下文。

3

這與導致S/MIME簽名和XML簽名問題的問題相同。也就是說,要簽名的數據有多個等價的表示形式。

例如,在JSON:

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

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

或者根據您的應用程序,這甚至可能是等價的:

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

規範化可以解決這個問題,但這是一個你根本不需要的問題。

如果您對規範有控制權,簡單的解決方案是將對象包裝在某種容器中,以防止它轉化爲「等效」但不同表示。

I.e.通過不簽署「邏輯」對象來避免這個問題,而是用一個特定的序列化表示來代替它。

例如,JSON對象 - > UTF-8文本 - >字節。將字節標記爲字節,然後將它們作爲字節傳送給,例如,通過base64編碼。由於您正在對字節進行簽名,因此諸如空白之類的差異是簽名的一部分。

,而不是試圖做到這一點:

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

只是這樣做:

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

} 

即請勿簽署JSON,請在編碼的 JSON的字節上簽字

是的,這意味着簽名不再透明。

+0

Pro:這將放鬆屬性的耦合,如「可選」對象所示。 小調:標準API工具不理解此包裝。再次,爲這些生產哈希不是微不足道的。 – LexieHankins

+1

自從我關注這個問題已經有好幾年了,但是如果我今天不得不實施哈希,這是我會採取的方法。 –