web-dev-qa-db-ja.com

JSONオブジェクトを暗号化してハッシュする方法は?

次の質問は、最初に思われるよりも複雑です。

他のネストされたJSONオブジェクトを含む任意の量のデータを含む可能性のある任意のJSONオブジェクトがあると仮定します。私が欲しいのは、実際のJSONフォーマット自体に関係なく、JSONデータの暗号化ハッシュ/ダイジェストです(例:改行やJSONトークン間のスペースの違いを無視する)。

JSONはさまざまなプラットフォームのさまざまな(デ)シリアライザーによって生成/読み取りされるため、最後の部分は要件です。 Java用の少なくとも1つのJSONライブラリは、逆シリアル化中にデータを読み取るときにフォーマットを完全に削除します。そのため、ハッシュが壊れます。

上記の任意のデータ句は、既知のフィールドを特定の順序で取得してから、それらを保持する前に連結することができないため、状況も複雑にします(Javaの非暗号化hashCode()メソッドがどのように機能するかを大まかに考えてください)。

最後に、JSON文字列全体を(逆シリアル化の前に)バイトのチャンクとしてハッシュすることも望ましくありません。ハッシュの計算時に無視する必要があるフィールドがJSONにあるためです。

この問題に適切な解決策があるかどうかはわかりませんが、アプローチや考えを歓迎します=)

52
Jason Nichols

この問題は、柔軟性が許容されるあらゆるデータ形式のハッシュを計算するときによくある問題です。これを解決するには、表現をcanonicalizeする必要があります。

たとえば、Twitterやその他のサービスが認証に使用するOAuth1.0aプロトコルでは、リクエストメッセージの安全なハッシュが必要です。ハッシュを計算するには、OAuth1.0aは最初にフィールドをアルファベット順に並べ、改行で区切り、フィールド名(よく知られている)を削除し、空の値に空白行を使用する必要があると述べています。署名またはハッシュは、その正規化の結果に基づいて計算されます。

XML DSIGも同じように機能します-署名する前にXMLを正規化する必要があります。 これをカバーする提案されたW3標準 があります。これは、署名のためのそのような基本的な要件だからです。一部の人々はそれをc14nと呼びます。

Jsonの正規化標準については知りません。調査する価値があります。

ない場合は、特定のアプリケーションの使用に関する規則を確立できます。適切な開始は次のようになります。

  • プロパティを辞書式に名前でソートする
  • すべての名前に使用される二重引用符
  • すべての文字列値で使用される二重引用符
  • 名前とコロンの間、およびコロンと値の間にスペースがない、または1つのスペースがある
  • 値とそれに続くコンマの間にスペースを入れない
  • 他のすべての空白は、単一のスペースまたは何もない状態に縮小されています-1つ選択してください
  • 署名したくないプロパティを除外します(1つの例は、署名自体を保持するプロパティです)。
  • 選択したアルゴリズムで結果に署名する

また、JSONオブジェクトでその署名を渡す方法を検討することもできます。「nichols-hmac」などのよく知られているプロパティ名を確立して、base64でエンコードされたバージョンのハッシュを取得することもできます。このプロパティは、ハッシュアルゴリズムによって明示的に除外する必要があります。その後、JSONの受信者はハッシュをチェックできます。

正規化された表現は、アプリケーションで渡す表現である必要はありません。任意のJSONオブジェクトを指定して簡単に作成する必要があるだけです。

43
Cheeso

独自のJSON正規化/正規化を発明する代わりに、 bencode を使用することができます。意味的にはJSON(数値、文字列、リスト、辞書の構成)と同じですが、暗号化ハッシュに必要な明確なエンコーディングのプロパティがあります。

bencodeはtorrentファイル形式として使用され、すべてのbittorrentクライアントには実装が含まれています。

5
Nikita Nemkin

これは、S/MIME署名およびXML署名で問題が発生するのと同じ問題です。つまり、署名されるデータの同等の表現が複数あります。

たとえば、JSONの場合:

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

vs.

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

または、アプリケーションによっては、これは同等の場合もあります。

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

正規化はその問題を解決することができますが、それはあなたがまったく必要としない問題です。

仕様を制御できる場合の簡単な解決策は、オブジェクトをある種のコンテナーにラップして、「同等」の異なる表現に変換されないようにすることです。

つまり「論理」オブジェクトに署名せず、代わりにそのオブジェクトの特定の直列化表現に署名することにより、問題を回避します。

たとえば、JSONオブジェクト-> UTF-8テキスト->バイト。バイトに署名バイトとして、次にそれらを送信バイトとして例: base64エンコーディング。バイトに署名しているため、空白などの違いは署名対象の一部です。

これを試みる代わりに:

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

これを行うだけです:

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

}

つまりJSONに署名せず、エンコードされたバイト数 JSONに署名します。

はい、署名はもはや透過的ではありません。

4
Ben

JSON-LD 正規化を行うことができます。

コンテキストを定義する必要があります。

3
jbaylina

RFC 7638:JSON Web Key(JWK)サムプリントには、一種の正規化が含まれています。 RFC7638は限られたメンバーのセットを想定していますが、どのメンバーにも同じ計算を適用できます。

https://tools.ietf.org/html/rfc7638#section-

1
Dai MIKURUBE

JSONエンコードされたペイロードのハッシュに関する簡単な問題が発生しました。私たちのケースでは、次の方法論を使用します。

  1. データをJSONオブジェクトに変換します。
  2. JSONペイロードをbase64でエンコードする
  3. 生成されたbase64ペイロードのメッセージダイジェスト(HMAC)。
  4. Base64ペイロードを送信します。

このソリューションを使用する利点:

  1. Base64は、指定されたペイロードに対して同じ出力を生成します。
  2. 結果の署名はbase64でエンコードされたペイロードから直接派生し、base64-payloadはエンドポイント間で交換されるため、署名とペイロードは維持されます。
  3. このソリューションは、特殊文字のエンコーディングの違いにより発生する問題を解決します。

短所

  1. ペイロードのエンコード/デコードはオーバーヘッドを追加する可能性があります
  2. Base64でエンコードされたデータは通常、元のペイロードより30 +%大きくなります。
0

すべてのフィールドを所定の順序で実行します(アルファベット順など)。なぜ任意のデータが違いを生むのですか?プロパティを繰り返し処理するだけです(反射)。

または、生のjson文字列をいくつかの明確に定義された正規形式に変換し(余分なフォーマットをすべて削除します)、それをハッシュします。

0
RasmusKL