pérez nicole mosa hanna grace canciones ashley aprendí json cryptography canonicalization

json - grace - hanna nicole pérez mosa



¿Cómo criptográficamente hash un objeto JSON? (6)

La siguiente pregunta es más compleja de lo que parece a primera vista.

Supongamos que tengo un objeto JSON arbitrario, uno que puede contener cualquier cantidad de datos, incluidos otros objetos JSON anidados. Lo que quiero es un hash / resumen criptográfico de los datos JSON, sin tener en cuenta el formato JSON real (p. Ej., Ignorar las nuevas líneas y las diferencias de espaciado entre los tokens JSON).

La última parte es un requisito, ya que el JSON será generado / leído por una variedad de (des) serializadores en varias plataformas diferentes. Conozco al menos una biblioteca JSON para Java que elimina completamente el formato al leer datos durante la deserialización. Como tal romperá el hash.

La cláusula de datos arbitrarios de arriba también complica las cosas, ya que me impide tomar campos conocidos en un orden determinado y concatenarlos antes de realizar el hasing (piense aproximadamente cómo funciona el método de código de hash no criptográfico de Java).

Por último, el hash de toda la cadena JSON como un trozo de bytes (antes de la deserialización) tampoco es deseable, ya que hay campos en el JSON que deben ignorarse al calcular el hash.

No estoy seguro de que haya una buena solución para este problema, pero acojo con agrado cualquier enfoque o pensamiento =)


El problema es común cuando se calculan hashes para cualquier formato de datos donde se permita la flexibilidad. Para resolver esto, necesitas canonizar la representación.

Por ejemplo, el protocolo OAuth1.0a, que es usado por Twitter y otros servicios para la autenticación, requiere un hash seguro del mensaje de solicitud. Para calcular el hash, OAuth1.0a dice que primero debe alfabetizar los campos, separarlos por nuevas líneas, eliminar los nombres de los campos (que son bien conocidos) y usar líneas en blanco para los valores vacíos. La firma o hash se calcula sobre el resultado de esa canonicalización.

XML DSIG funciona de la misma manera: debe canonizar el XML antes de firmarlo. Hay un estándar W3 propuesto que cubre esto , porque es un requisito fundamental para la firma. Algunas personas lo llaman c14n.

No sé de un estándar de canonicalización para json. Vale la pena investigar.

Si no hay uno, puede establecer una convención para el uso de su aplicación en particular. Un comienzo razonable podría ser:

  • Ordenar lexicográficamente las propiedades por nombre
  • comillas dobles utilizadas en todos los nombres
  • comillas dobles utilizadas en todos los valores de cadena
  • sin espacio, o un espacio, entre los nombres y los dos puntos, y entre los dos puntos y el valor
  • Sin espacios entre valores y la siguiente coma.
  • todos los demás espacios en blanco se colapsaron en un solo espacio o en nada: elija uno
  • excluya cualquier propiedad que no quiera firmar (un ejemplo es la propiedad que contiene la firma en sí)
  • Firma el resultado, con tu algoritmo elegido.

También puede pensar cómo pasar esa firma en el objeto JSON; posiblemente establezca un nombre de propiedad conocido, como "nichols-hmac" o algo así, que obtenga la versión codificada en base64 del hash. Esta propiedad debería ser excluida explícitamente por el algoritmo de hash. Entonces, cualquier receptor del JSON podría verificar el hash.

La representación canonizada no tiene que ser la representación que se pasa en la aplicación. Solo necesita ser producido fácilmente dado un objeto JSON arbitrario.


En lugar de inventar su propia normalización / canonicalización JSON, es posible que desee utilizar bencode . Semánticamente es lo mismo que JSON (composición de números, cadenas, listas y dados), pero con la propiedad de codificación inequívoca que es necesaria para el hashing criptográfico.

bencode se usa como un formato de archivo torrent, cada cliente bittorrent contiene una implementación.


Encontramos un problema simple con el hash codificado con JSON. En nuestro caso utilizamos la siguiente metodología:

  1. Convertir datos en objeto JSON;
  2. Codificar la carga útil de JSON en base64
  3. Mensaje resumido (HMAC) la carga útil base64 generada.
  4. Transmitir carga base64.

Ventajas de utilizar esta solución:

  1. Base64 producirá la misma salida para una carga útil dada.
  2. Dado que la firma resultante se derivará directamente de la carga útil codificada en base64 y como la carga útil en base64 se intercambiará entre los puntos finales, estaremos seguros de que la firma y la carga útil se mantendrán.
  3. Esta solución resuelve los problemas que surgen debido a la diferencia en la codificación de caracteres especiales.

Desventajas

  1. La codificación / decodificación de la carga útil puede agregar sobrecarga
  2. Los datos codificados en base64 suelen ser un 30% más grandes que la carga útil original.

Este es el mismo problema que causa problemas con las firmas S / MIME y las firmas XML. Es decir, hay múltiples representaciones equivalentes de los datos a firmar.

Por ejemplo en JSON:

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

contra

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

O dependiendo de su aplicación, esto puede ser equivalente:

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

La canonización podría resolver ese problema, pero es un problema que no necesita en absoluto.

La solución fácil si tiene control sobre la especificación es envolver el objeto en algún tipo de contenedor para evitar que se transforme en una representación "equivalente" pero diferente.

Es decir, evitar el problema al no firmar el objeto "lógico" sino a una representación serializada particular de él.

Por ejemplo, Objetos JSON -> Texto UTF-8 -> Bytes. Firme los bytes como bytes , luego transmítalos como bytes, por ejemplo, mediante codificación base64. Dado que está firmando los bytes, las diferencias como los espacios en blanco son parte de lo que está firmado.

En lugar de tratar de hacer esto:

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

Solo haz esto:

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

Es decir, no firma el JSON, firma los bytes del JSON codificado .

Sí, significa que la firma ya no es transparente.


Haría todos los campos en un orden dado (alfabéticamente por ejemplo). ¿Por qué los datos arbitrarios hacen una diferencia? Puede simplemente iterar sobre las propiedades (ala reflexión).

Alternativamente, me gustaría convertir la cadena json sin procesar en una forma canónica bien definida (eliminar todo el formato superflous) - y hacer "hashing".


JSON-LD puede hacer la normalización.

Tendrás que definir tu contexto.