XML y canonicalización

José Miguel Moreno
3 min readJun 26, 2018

--

Hace unas semanas, trabajando en una nueva funcionalidad para la librería Facturae-PHP, tuve la necesidad de implementar un tipo de procesado de XML esencial para la realización de firmas electrónicas: la canonicalización.

Canonicalizar un documento XML consiste en realizar un procesamiento que permita obtener su forma canónica. Esta representación pretende ser una “normalización” del documento original, de forma que a partir de un árbol de elementos anidados se genere siempre el mismo código en XML.

De entre los distintos métodos de canonicalización existentes, en este artículos utilizaremos C14N (inclusivo) por ser uno de los más extendido.

Reglas básicas de canonicalización en XML

Véamos un ejemplo para entender mejor este concepto. Partimos del siguiente documento que representa a un pedido:

<?xml version="1.0" encoding="UTF-8"?>
<Pedido xmlns="http://espac.io/ped"
xmlns:c="http://espac.io/co">
<Cliente puntos='17' c:tieneComentarios="si" id="124AU">
<Nombre>Fulano de Tal</Nombre>
<!-- No tenemos más datos -->
</Cliente>
<Productos>
<Producto>
<Nombre>Hamburguesa especial</Nombre>
<Precio>12.49</Precio>
<Pagado/>
</Producto>
</Productos>
<c:Comentarios>
<![CDATA[Tiempo de cocinado > 8 minutos]]>
</c:Comentarios>
</Pedido>

Si le aplicamos una canonicalización C14N obtenemos el siguiente resultado:

<Pedido xmlns="http://espac.io/ped" xmlns:c="http://espac.io/co">
<Cliente c:tieneComentarios="si" id="124AU" puntos="17">
<Nombre>Fulano de Tal</Nombre>

</Cliente>
<Productos>
<Producto>
<Nombre>Hamburguesa especial</Nombre>
<Precio>12.49</Precio>
<Pagado></Pagado>
</Producto>
</Productos>
<c:Comentarios>
Tiempo de cocinado &gt; 8 minutos
</c:Comentarios>
</Pedido>

Como puedes ver, el código XML ha cambiado. Concretamente, se han realizado las siguientes transformaciones:

  • Antes de nada, la línea <?xml version="1.0" encoding="UTF-8"?> desaparece por completo. Esto se debe a que los documentos canonicalizados siempre se codifican en UTF-8.
  • El salto de línea entre xmlns y xmlns:c se elimina, pues ambos campos están dentro de la declaración de un elemento.
  • Los atributos de <Cliente/> sufren varias modificaciones, cambiando las comillas simples por dobles, eliminando dobles espacios (y saltos de línea si los hubiera) y ordenando los elementos por orden lexicográfico. Luego veremos esta última transformación en más detalle.
  • Los bloques de comentarios desaparecen, conservando saltos de línea y espacios en blanco.
  • Los elementos vacíos (como <Pagado/> ) se expanden.
  • Todos los bloques de <![CDATA[]]> se codifican escapando los caracteres especiales.

Orden lexicográfico

Como decíamos antes, los atributos de un nodo XML se ordenan de una forma un tanto especial que se asemeja a la alfabética. Este orden recibe el nombre de lexiocográfico.

Supongamos que un elemento tiene los siguientes espacios de nombres y atributos:

  • xmlns="http://espac.io/principal"
  • xmlns:a="http://espac.io/a"
  • xmlns:b="http://espac.io/b"
  • Id="12345"
  • a:precio="12.34"
  • a:descuento="0.23"
  • b:abierto="si"
  • b:cerrado="no"

Para ordenarlos lexicológicamente si siguen las siguientes reglas:

  1. Se coloca el namespace del elemento (xmlns) como el primero de todos.
  2. Después se añaden el resto de declaraciones de espacios de nombres por orden alfabético (xmlns:a antes que xmlns:b).
  3. A continuación se incluyen los unqualified attributes (los que van sin espacio de nombres declarado) en orden alfabético. En este caso, solo Id .
  4. Por último, se añaden el resto de atributos (qualified attributes) también ordenados de forma alfabética.

El orden final sería, por tanto, el que sigue:

xmlns="http://espac.io/principal"
xmlns:a="http://espac.io/a"
xmlns:b="http://espac.io/b"
Id="12345"
a:descuento="0.23"
a:precio="12.34"
b:abierto="si"
b:cerrado="no"

Conclusiones y casos de uso

Llegados a este punto te estarás preguntando: ¿y esto qué utilidad tiene?

Lo cierto es que establecer unos estándares para que documentos XML con el mismo contenido tenga el mismo formato es más importante de lo que parece. La principal aplicación de ello se da en XMLDsig, que permite garantizar la integridad y autoría de un documento XML al firmarlo electrónicamente.

Para comprobar la integridad se toma el contenido que se quiere firmar y se calcula su hash (digest). Como las funciones hash son algoritmos de un solo sentido, para verificar que el hash es correcto el destinatario debe hacer el mismo proceso a partir del mensaje que recibe.

Al canonicalizar el contenido antes de calcular su digest nos aseguramos que siempre obtendremos el mismo resultado para el mismo mensaje, independientemente de cómo este representado en XML.

Ten en cuenta que este artículo es solo una introducción a la canonicalización en XML. Si quieres obtener más información sobre el tema, te recomiendo que consultes la especificación de C14N del W3C, la cual expone todos los aspectos a tener en cuenta a la hora de obtener la forma canónica de un documento.

--

--