62 votes

Comment hacher cryptographiquement un objet JSON ?

La question suivante est plus complexe qu'il n'y paraît à première vue.

Supposons que j'ai un objet JSON arbitraire, qui peut contenir n'importe quelle quantité de données, y compris d'autres objets JSON imbriqués. Ce que je veux, c'est un hachage/digesté cryptographique des données JSON, sans tenir compte du formatage JSON lui-même (par exemple, ignorer les nouvelles lignes et les différences d'espacement entre les jetons JSON).

La dernière partie est une exigence, car le JSON sera généré/lu par une variété de (dé)sérialiseurs sur un certain nombre de plateformes différentes. Je connais au moins une bibliothèque JSON pour Java qui supprime complètement le formatage lors de la lecture des données pendant la désérialisation. En tant que telle, elle brisera le hachage.

La clause de données arbitraires ci-dessus complique également les choses, car elle m'empêche de prendre des champs connus dans un ordre donné et de les concaténer avant le hachage (pensez à la façon dont fonctionne la méthode non cryptographique hashCode() de Java).

Enfin, il n'est pas non plus souhaitable de hacher la totalité de la chaîne JSON en tant que bloc d'octets (avant la désérialisation), car certains champs du JSON doivent être ignorés lors du calcul du hachage.

Je ne suis pas sûr qu'il existe une bonne solution à ce problème, mais toute approche ou réflexion est la bienvenue =)

1 votes

Avez-vous jeté un coup d'œil au DSig XML ? Ils ont le même problème et ont une spécification de "canonicalisation" assez complexe.

5 votes

Je ne peux m'empêcher de remarquer à quel point votre nom correspond à la question.

6 votes

Ceci est en cours de normalisation. Voir le projet de RFC sur la signature Web JSON (JWS). tools.ietf.org/html/draft-ietf-jose-json-web-signature-17

54voto

Cheeso Points 87022

Le problème est courant lors du calcul de hachages pour tout format de données où la flexibilité est autorisée. Pour le résoudre, il faut canoniser la représentation.

Par exemple, le protocole OAuth1.0a, qui est utilisé par Twitter et d'autres services pour l'authentification, exige un hachage sécurisé du message de demande. Pour calculer le hachage, OAuth1.0a indique qu'il faut d'abord classer les champs par ordre alphabétique, les séparer par des nouvelles lignes, supprimer les noms des champs (qui sont bien connus) et utiliser des lignes vides pour les valeurs vides. La signature ou le hachage est calculé sur le résultat de cette canonisation.

XML DSIG fonctionne de la même manière - vous devez canoniser le XML avant de le signer. Il existe un proposition de norme W3 couvrant cette parce que c'est une exigence fondamentale pour la signature. Certaines personnes l'appellent c14n.

Je ne connais pas de norme de canonisation pour Json. Cela vaut la peine de faire des recherches.

S'il n'y en a pas, vous pouvez certainement établir une convention pour l'utilisation de votre application particulière. Un début raisonnable pourrait être :

  • trier lexicographiquement les propriétés par nom
  • les guillemets doubles sont utilisés pour tous les noms
  • guillemets doubles utilisés sur toutes les valeurs de chaîne
  • pas d'espace, ou un espace, entre les noms et le deux-points, et entre le deux-points et la valeur
  • pas d'espace entre les valeurs et la virgule suivante
  • tous les autres espaces blancs se réduisent à un seul espace ou à rien - choisissez-en un
  • exclure toutes les propriétés que vous ne voulez pas signer (par exemple, la propriété qui contient la signature elle-même).
  • signer le résultat, avec l'algorithme que vous avez choisi

Vous pouvez également réfléchir à la manière de transmettre cette signature dans l'objet JSON - éventuellement établir un nom de propriété bien connu, comme "nichols-hmac" ou autre, qui obtient la version codée en base64 du hachage. Cette propriété devrait être explicitement exclue par l'algorithme de hachage. Ensuite, tout récepteur du JSON serait en mesure de vérifier le hachage.

La représentation canonisée ne doit pas nécessairement être la représentation que vous faites circuler dans l'application. Il suffit qu'elle soit facilement produite à partir d'un objet JSON arbitraire.

4 votes

La canonisation doit également tenir compte de la représentation des caractères : "A" vs "\u0041" , "é" vs "\u00e9" vs "\u00E9" . Même problème pour les numéros : 1 vs 0.1e1 .

1 votes

La canonisation doit également tenir compte des chiffres. ECMAscript définit JSON.stringify, qui indique qu'il faut formater un nombre sans exposant s'il est compris dans la plage [1e-6, 1e21] ; sinon, il faut le formater avec un chiffre avant la virgule.

9voto

Nikita Nemkin Points 1515

Au lieu d'inventer votre propre normalisation/canonisation JSON, vous pouvez utiliser bencode . D'un point de vue sémantique, c'est la même chose que JSON (composition de nombres, de chaînes de caractères, de listes et de dicts), mais avec la propriété d'encodage non ambiguë qui est nécessaire pour le hachage cryptographique.

bencode est utilisé comme un format de fichier torrent, chaque client bittorrent contient une implémentation.

0 votes

JSON est largement préféré car presque tous les langages disposent de bibliothèques permettant de (dé)sérialiser les objets.

4 votes

Je voulais dire utiliser le bencode uniquement comme une étape de normalisation avant le hachage. En dehors de votre routine de hachage, tout reste en JSON.

0 votes

Le bencode est génial et super facile à mettre en œuvre. Le JSON canonique n'est pas non plus analysé par un analyseur JSON standard. Aucun des deux n'a besoin d'être analysé pour cette application qui ne nécessite qu'une fonction de hachage en entrée.

8voto

Ben Points 14995

Il s'agit du même problème que celui qui se pose avec les signatures S/MIME et les signatures XML. En effet, il existe plusieurs représentations équivalentes des données à signer.

Par exemple en JSON :

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

vs.

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

Ou, selon votre application, cela peut même être équivalent :

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

La canonisation pourrait résoudre ce problème, mais c'est un problème dont vous n'avez pas du tout besoin.

La solution facile, si vous avez le contrôle de la spécification, est d'envelopper l'objet dans une sorte de conteneur pour le protéger contre la transformation en une représentation "équivalente" mais différente.

C'est-à-dire éviter le problème en ne signant pas l'objet "logique" mais en signant une représentation sérialisée particulière de celui-ci.

Par exemple, Objets JSON -> Texte UTF-8 -> Octets. Signez les octets en octets puis les transmettre en octets par exemple, par codage base64. Puisque vous signez les octets, les différences comme les espaces font partie de ce qui est signé.

Au lieu d'essayer de faire ça :

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

Fais juste ça :

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

}

C'est-à-dire ne pas signer le JSON, signer les octets de l'encodage JSON.

Oui, cela signifie que la signature n'est plus transparente.

0 votes

Pro : Cela desserre le couplage pour les propriétés, comme l'indique votre objet "Optionnel". Petit inconvénient : Les outils API standard ne comprennent pas ce conditionnement. Mais là encore, produire des hachages pour ces objets n'est pas trivial.

4 votes

Cela fait des années que je ne me suis pas penché sur ce problème, mais si je devais mettre en œuvre le hachage aujourd'hui, voici l'approche que j'adopterais.

3voto

jbaylina Points 1241

JSON-LD peut faire de la normalisation.

Vous devrez définir votre contexte.

2voto

Dai MIKURUBE Points 65

RFC 7638 : JSON Web Key (JWK) Thumbprint comprend un type de canonisation. Bien que la RFC7638 prévoit un ensemble limité de membres, nous serions en mesure d'appliquer le même calcul pour n'importe quel membre.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X