web-dev-qa-db-fra.com

Comment chiffrer 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, un objet pouvant contenir n’importe quelle quantité de données, y compris d’autres objets JSON imbriqués. Ce que je veux, c'est un hachage/résumé 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 plates-formes différentes. Je connais au moins une bibliothèque JSON pour Java qui supprime complètement le formatage lors de la lecture de données pendant la désérialisation. En tant que tel, il va casser le hash.

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 hasing (réfléchissez à la façon dont fonctionne la méthode hashCode () non cryptographique de Java).

Enfin, le hachage de la chaîne JSON entière sous forme de bloc d'octets (avant la désérialisation) n'est pas souhaitable non plus, car il existe des champs dans le JSON qui doivent être ignorés lors du calcul du hachage.

Je ne suis pas sûr qu'il y ait une bonne solution à ce problème, mais j'apprécie toute approche ou pensée =)

49
Jason Nichols

Le problème est commun lors du calcul de hachages pour tout format de données où la flexibilité est autorisée. Pour résoudre ce problème, vous devez - canonicalize la représentation.

Par exemple, le protocole OAuth1.0a, utilisé par Twitter et d'autres services pour l'authentification, nécessite un hachage sécurisé du message de requête. Pour calculer le hachage, OAuth1.0a indique que vous devez d'abord alphabétiser les champs, les séparer par des lignes, supprimer les noms de champs (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 norme W3 proposée couvrant ce sujet , car il s'agit d'une exigence fondamentale pour la signature. Certaines personnes l'appellent c14n. 

Je ne connais pas de standard 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 lexxographiquement les propriétés par nom
  • guillemets doubles utilisés sur tous les noms
  • guillemets doubles utilisés sur toutes les valeurs de chaîne
  • pas d'espace, ou un espace, entre les noms et les deux points, et entre les deux points et la valeur 
  • pas d'espaces entre les valeurs et la virgule suivante
  • tous les autres espaces sont réduits à un seul espace ou à rien - choisissez-en un 
  • exclure 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 choisi

Vous voudrez peut-être aussi réfléchir à la manière de passer cette signature dans l'objet JSON - éventuellement, créez un nom de propriété bien connu, tel que "nichols-hmac" ou quelque chose d'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 destinataire du JSON pourrait vérifier le hachage. 

La représentation canonisée ne doit pas nécessairement être la représentation que vous transmettez dans l'application. Il doit seulement être facilement produit avec un objet JSON arbitraire. 

43
Cheeso

Au lieu d’inventer votre propre normalisation/canonisation JSON, vous pouvez utiliser bencode . Sémantiquement, il s'agit de la même chose que JSON (composition de nombres, de chaînes, de listes et de dict), mais avec la propriété d'un codage sans ambiguïté nécessaire au hachage cryptographique.

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

5
Nikita Nemkin

Ce problème est identique à celui qui pose des problèmes avec les signatures S/MIME et les signatures XML. C'est-à-dire qu'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 canonicalisation pourrait résoudre ce problème, mais c'est un problème dont vous n'avez pas besoin du tout.

La solution facile si vous avez le contrôle sur la spécification consiste à envelopper l'objet dans une sorte de conteneur pour le protéger de toute 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 sous forme d’octets , puis transmettez-les sous forme d’octets ex. par encodage base64. Étant donné que vous signez les octets, les différences telles que les espaces font partie de ce qui est signé.

Au lieu d'essayer de faire ceci:

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

Faites juste ceci:

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

}

C'est à dire. ne signez pas le JSON, signe les octets du codé JSON.

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

4
Ben

JSON-LD peut faire la normalisation. 

Vous devrez définir votre contexte.

2
jbaylina

RFC 7638: clé JSON Web Key (JWK) comprend un type de canonisation. Bien que la RFC7638 attend un nombre 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

1
Dai MIKURUBE

Nous avons rencontré un problème simple avec le hachage de charges utiles codées JSON. Dans notre cas, nous utilisons la méthodologie suivante:

  1. Convertir des données en objet JSON;
  2. Encoder la charge JSON en base64 
  3. Message digest (HMAC) la charge utile générée en base64. 
  4. Transmettre la charge base64. 

Avantages de l'utilisation de cette solution:

  1. Base64 produira la même sortie pour une charge utile donnée.
  2. Puisque la signature résultante sera directement dérivée de la charge codée en base64 et que la charge base64 sera échangée entre les points d'extrémité, nous serons certains que la signature et la charge utile seront conservées. 
  3. Cette solution résout les problèmes liés à la différence de codage des caractères spéciaux. 

Désavantages  

  1. Le codage/décodage de la charge utile peut ajouter une surcharge 
  2. Les données codées en Base64 ont généralement une taille supérieure de 30% à la charge utile initiale.
0
Deezzle LuBimkii

Je ferais tous les champs dans un ordre donné (alphabétiquement par exemple). Pourquoi les données arbitraires font-elles une différence? Vous pouvez simplement parcourir les propriétés (ala réflexion).

Alternativement, je chercherais à convertir la chaîne brute json en une forme canonique bien définie (supprimer tout formatage superflous) - et à la hacher.

0
RasmusKL