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 =)
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:
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.
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.
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.
JSON-LD peut faire la normalisation.
Vous devrez définir votre contexte.
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.
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:
Avantages de l'utilisation de cette solution:
Désavantages
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.