2011-01-12 16 views
44

Die folgende Frage ist komplexer als es zunächst scheinen mag.Wie verschlüsselt man ein JSON-Objekt kryptographisch?

Angenommen, ich habe ein beliebiges JSON-Objekt, das eine beliebige Menge an Daten enthalten kann, einschließlich anderer verschachtelter JSON-Objekte. Was ich will, ist ein kryptographischer Hash/Digest der JSON-Daten, ohne Rücksicht auf die eigentliche JSON-Formatierung selbst (zB: Ignorieren von Zeilenumbrüchen und Abstandsunterschieden zwischen den JSON-Tokens).

Der letzte Teil ist eine Voraussetzung, da der JSON von einer Vielzahl von (De-) Serialisierern auf verschiedenen Plattformen erzeugt/gelesen wird. Ich kenne mindestens eine JSON-Bibliothek für Java, die beim Lesen von Daten während der Deserialisierung die Formatierung vollständig entfernt. Als solches wird es den Hash-Wert brechen. Die obige willkürliche Datenklausel verkompliziert auch die Dinge, da sie mich daran hindert, bekannte Felder in einer gegebenen Reihenfolge zu nehmen und sie vor dem Hashen zu verketten (denke ungefähr daran, wie Javas nicht-kryptografische Methode hashCode() funktioniert).

Schließlich ist das Hashing des gesamten JSON-Strings als ein Byte-Chunk (vor der Deserialisierung) auch nicht wünschenswert, da es Felder im JSON gibt, die beim Berechnen des Hash ignoriert werden sollten.

Ich bin mir nicht sicher, es ist eine gute Lösung für dieses Problem, aber ich begrüße alle Ansätze und Gedanken =)

+3

Ich kann nicht helfen, aber beachten Sie, wie passend Ihr Name zu der Frage ist. –

+1

Haben Sie sich die XML DSig angesehen? Sie haben das gleiche Problem und haben eine ziemlich komplexe "canonicalization" Spezifikation. – mtraut

+5

Dies wird standardisiert. Weitere Informationen finden Sie im JSON-Web-Signatur (JWS) -Entwurfs-RFC. http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-17 – user239558

Antwort

38

Das Problem ist ein häufiges Problem beim Berechnen von Hashwerten für alle Datenformate, bei denen Flexibilität zulässig ist. Um dies zu lösen, müssen Sie die Darstellung kanonisieren.

Zum Beispiel erfordert das OAuth1.0a-Protokoll, das von Twitter und anderen Diensten zur Authentifizierung verwendet wird, einen sicheren Hash der Anforderungsnachricht. Um den Hash zu berechnen, müssen Sie in OAuth1.0a zuerst die Felder alphabetisch sortieren, sie durch Zeilenumbrüche trennen, die Feldnamen entfernen (die allgemein bekannt sind) und Leerzeilen für leere Werte verwenden. Die Signatur oder der Hash wird auf dem Ergebnis dieser Kanonisierung berechnet.

XML DSIG funktioniert auf die gleiche Weise - Sie müssen das XML vor dem Signieren kanonisieren. Es gibt eine proposed W3 standard covering this, weil es so eine grundlegende Voraussetzung für das Signieren ist. Manche Leute nennen es c14n.

Ich kenne keinen Kanonisierungsstandard für Json. Es lohnt sich zu recherchieren.

Wenn es keinen gibt, können Sie sicherlich eine Konvention für Ihre spezielle Anwendungsnutzung festlegen.Eine vernünftige Start könnte sein:

  • lexikografisch die Eigenschaften von Namen auf alle Zeichenfolge verwendet
  • doppelte Anführungszeichen auf allen Namen
  • doppelte Anführungszeichen verwendet Sortierwerte
  • kein Raum oder einem Raum, zwischen Namen und der Doppelpunkt, und zwischen dem Doppelpunkt und dem Wert
  • keine Leerzeichen zwischen den Werten und dem folgenden Komma
  • alle anderen Leerraum zusammengebrochen, um entweder ein einzelnes Leerzeichen oder nichts - wählen Sie eine
  • schließen alle Eigenschaften, die Sie wollen (die Eigenschaft, ein Beispiel dafür ist, dass die Unterschrift selbst hält) nicht unterschreiben
  • Zeichen das Ergebnis, mit dem gewählten Algorithmus

Sie können auch denken, wie um diese Signatur im JSON-Objekt zu übergeben - eventuell einen bekannten Eigenschaftsnamen, wie "nichols-hmac" oder etwas, das die base64-kodierte Version des Hashs bekommt. Diese Eigenschaft müsste explizit durch den Hashalgorithmus ausgeschlossen werden. Dann könnte jeder Empfänger des JSON den Hash überprüfen.

Die kanonische Darstellung muss nicht die Darstellung sein, die Sie in der Anwendung weitergeben. Es muss nur einfach mit einem beliebigen JSON-Objekt erzeugt werden.

+2

Die Kanonisierung muss auch die Darstellung von Zeichen berücksichtigen: '" A "' vs '" \ u0041 "', '" é "' vs '" \ u00e9 "' vs '" \ u00e9 "'. Gleiches Problem für Zahlen: '1' vs' 0.1e1'. – dolmen

+1

Zum Beispiel https://github.com/jchris/canonical-json – opyate

+2

überprüfen Sie dies: https://github.com/substack/json-stable-stringify – jbaylina

0

Ich würde alle Felder in einer bestimmten Reihenfolge tun (in alphabetischer Reihenfolge zum Beispiel). Warum machen beliebige Daten einen Unterschied? Sie können einfach über die Eigenschaften (Ala-Reflexion) iterieren.

Alternativ würde ich in die Umwandlung der rohen JSON-Zeichenfolge in eine gut definierte kanonische Form (entfernen Sie alle überflüssige Formatierung) - und Hashing, dass.

5

Anstatt Ihre eigene JSON Normalisierung/Kanonisierung zu erfinden, können Sie bencode verwenden. Semantisch ist es dasselbe wie JSON (Zusammensetzung von Zahlen, Strings, Listen und Dicts), aber mit der Eigenschaft der eindeutigen Codierung, die für das kryptografische Hashing notwendig ist.

Bencode wird als Torrent-Dateiformat verwendet, jeder Bittorrent-Client enthält eine Implementierung.

+0

JSON wird sehr bevorzugt, da fast jede Sprache Bibliotheken zur Verfügung hat, um die Serialisierung von Objekten durchzuführen. –

+4

Ich meinte Bencode nur als Normalisierungsschritt vor dem Hashing zu verwenden. Außerhalb deiner Hashing-Routine bleibt alles JSON. –

+0

Bencode ist großartig und super einfach zu implementieren. Kanonical JSON wird auch nicht mit einem Standard-JSON-Parser parsen. Für diese Anwendung, die nur eine Eingabe der Hash-Funktion erfordert, muss nicht analysiert werden. – joeforker

2

JSON-LD kann normalization tun.

Sie müssen Ihren Kontext definieren.

3

Dies ist das gleiche Problem, das Probleme mit S/MIME-Signaturen und XML-Signaturen verursacht. Das heißt, es gibt mehrere äquivalente Darstellungen der zu signierenden Daten.

Zum Beispiel in JSON:

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

Kanonisierung dieses Problem lösen könnte,:

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

gegen

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

Oder je nach Anwendung, dies sogar gleichwertig sein kann aber es ist ein Problem, das Sie überhaupt nicht brauchen.

Die einfache Lösung, wenn Sie Kontrolle über die Spezifikation haben, besteht darin, das Objekt in eine Art Container zu verpacken, um es davor zu schützen, in eine "äquivalente", aber andere Darstellung umgewandelt zu werden.

I.e. Vermeiden Sie das Problem, indem Sie das "logische" Objekt nicht signieren, sondern stattdessen eine bestimmte serialisierte Darstellung signieren.

Zum Beispiel, JSON-Objekte -> UTF-8 Text -> Bytes. Unterzeichne die Bytes als Bytes, dann sende sie als Bytes, z.B. von Base64-Codierung. Da Sie die Bytes signieren, sind Unterschiede wie Leerzeichen Teil dessen, was signiert ist.

Anstatt zu versuchen, dies zu tun:

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

Genau dies tun:

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

} 

D.h. signieren Sie nicht den JSON, signieren Sie die Bytes des codierten JSON.

Ja, das bedeutet, dass die Signatur nicht mehr transparent ist.

+0

Pro: Dies löst die Kopplung für die Eigenschaften, wie von Ihrem "Optional" -Objekt angezeigt. Minor con: Standard-API-Tools verstehen diese Verpackung nicht. Andererseits ist das Produzieren von Hashes für diese nicht trivial. – LexieHankins

+1

Es ist Jahre her, dass ich mich auf dieses Problem konzentriert habe, aber wenn ich Hashing heute implementieren müsste, wäre dies der Ansatz, den ich wählen würde. –

Verwandte Themen