USANDO XMLSEC PARA FIRMAR ARCHIVOS XML
Firmar un documento XML es una tarea común en procesos que requieren autenticidad e integridad, como la generación de DTE (Documento Tributario Electrónico). Para esto, podemos usar la utilidad xmlsec, una herramienta disponible en sistemas Unix y también en Windows (versiones de 32 y 64 bits).
🛠 Instalación de xmlsec1
Para firmar archivos XML desde la línea de comandos, utilizamos la herramienta xmlsec1
. Esta utilidad está disponible en la mayoría de los sistemas operativos, tanto en entornos Unix como en Windows.
🐧 En Linux (Debian/Ubuntu):
🐧 En Fedora o derivados:
🍏 En macOS (usando Homebrew):
🐚 En sistemas Unix tipo BSD (FreeBSD, OpenBSD, etc.):
En FreeBSD, puedes instalarlo usando el sistema de ports o mediante paquetes binarios:
Desde ports:
O desde pkg:
🪟 En Windows:
Puedes descargar las versiones precompiladas desde:
👉 https://www.aleksey.com/xmlsec/download.html
Asegúrate de elegir la versión adecuada para tu sistema (32 o 64 bits). Luego:
-
Extrae el contenido (por ejemplo, en
C:\xmlsec
). -
Agrega la ruta del ejecutable (
xmlsec1.exe
) al PATH del sistema para facilitar su uso en la línea de comandos.
Procediendo a Firmar:
En este ejemplo, vamos a firmar un DTE. A continuación, describo los archivos involucrados:
-
dtesinfirmar.xml: El DTE sin firmar, el archivo base.
-
privatekey.pem: La clave privada asociada al certificado digital.
-
certprueba.pem: El certificado digital.
-
dtefirmado.xml: El archivo resultante, que es el DTE firmado.
Suponiendo que tenemos el siguiente DTE sin firmar, en este caso se asume que está timbrado:
<?xml version="1.0"?>
<DTE version="1.0">
<Documento ID="F60T33">
<Encabezado>
<IdDoc>
<TipoDTE>33</TipoDTE>
<Folio>215</Folio>
<FchEmis>2016-09-27</FchEmis>
</IdDoc>
<Emisor>
<RUTEmisor>76040308-3</RUTEmisor>
<RznSoc>ESTEBAN GABRIEL GUENUL ALMONACID SERVIC</RznSoc>
<GiroEmis>SERV INFORMATICOS</GiroEmis>
<Acteco>726000</Acteco>
<CdgSIISucur>1</CdgSIISucur>
<DirOrigen>URMENETA 305 OFIC 513</DirOrigen>
<CmnaOrigen>PUERTO MONTT</CmnaOrigen>
<CiudadOrigen>PUERTO MONTT</CiudadOrigen>
</Emisor>
<Receptor>
<RUTRecep>77813960-K</RUTRecep>
<RznSocRecep>AMULEN CONSULTORES LTDA</RznSocRecep>
<GiroRecep>ASESORIAS TRIBUTARIAS</GiroRecep>
<DirRecep>URMENETA 305 OFICINA 513</DirRecep>
<CmnaRecep>PUERTO MONTT</CmnaRecep>
<CiudadRecep>PUERTO MONTT</CiudadRecep>
</Receptor>
<Totales>
<MntNeto>350000</MntNeto>
<TasaIVA>19</TasaIVA>
<IVA>66500</IVA>
<MntTotal>416500</MntTotal>
</Totales>
</Encabezado>
<Detalle>
<NroLinDet>1</NroLinDet>
<CdgItem>
<TpoCodigo>INT</TpoCodigo>
<VlrCodigo>53232</VlrCodigo>
</CdgItem>
<NmbItem>SERVIDOR HP</NmbItem>
<DscItem/>
<QtyItem>1</QtyItem>
<PrcItem>350000</PrcItem>
<MontoItem>350000</MontoItem>
</Detalle>
<TED version="1.0">
<DD>
<RE>76040308-3</RE>
<TD>33</TD>
<F>215</F>
<FE>2016-09-27</FE>
<RR>77813960-K</RR>
<RSR>AMULEN CONSULTORES LTDA</RSR>
<MNT>416500</MNT>
<IT1>SERVIDOR HP</IT1>
<CAF version="1.0">
<DA>
<RE>76040308-3</RE>
<RS>ESTEBAN GABRIEL GUENUL ALMONACID SERVIC</RS>
<TD>33</TD>
<RNG>
<D>146</D>
<H>224</H>
</RNG>
<FA>2016-09-22</FA>
<RSAPK>
<M>1/TnH7b8TztZoMrDEbxmZ60yOh0npQk89Jz1fuTxjP98ZytLUhG3N9wxfVCVipJ/FVZ3eMIHCIrDo4jyj5wa2Q==</M>
<E>Aw==</E>
</RSAPK>
<IDK>100</IDK>
</DA>
<FRMA algoritmo="SHA1withRSA">tPt4y4aa0irNHKGSpduUrxz4dLBo6Q8iVEGAFerogze84vduzQD7vpybllVXtqsab/qIa1UNHS2URvjvF198Yw==</FRMA>
</CAF>
<TSTED>2016-09-27T09:22:21</TSTED>
</DD>
<FRMT algoritmo="SHA1withRSA">HGhR/yVTW1m0DsQBKeOKkMfBCHt6pfKh0GiNKZWRHPntqYPRekgx3gbpquoak3Ts9b49lbJG0s35ZcbLo+miWg==</FRMT>
</TED>
<TmstFirma>2016-09-27T09:22:21</TmstFirma>
</Documento>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<Reference URI="#F60T33">
<Transforms>
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue/>
</Reference>
</SignedInfo>
<SignatureValue/>
<KeyInfo>
<KeyValue/>
<X509Data>
<X509Certificate/>
</X509Data>
</KeyInfo>
</Signature>
</DTE>
Ejecutamos el siguiente comando:
xmlsec1 --sign --output dtefirmado.xml --privkey-pem privatekey.pem,certprueba.pem --id-attr:ID Documento dtesinfirmar.xml
Firmar el EnvioDTE
El ejemplo anterior firma el DTE, pero el archivo EnvioDTE debe firmarse de forma diferente, ya que la firma va en un nodo específico.
Asumiendo que ya colocamos el DTE dentro del EnvioDTE
<?xml version="1.0" encoding="ISO-8859-1"?>
<envio:EnvioDTE xmlns="http://www.sii.cl/SiiDte" xmlns:envio="http://www.sii.cl/SiiDte" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sii.cl/SiiDte EnvioDTE_v10.xsd" version="1.0">
<SetDTE ID="SetDoc">
<Caratula version="1.0">
<RutEmisor>76040308-3</RutEmisor>
<RutEnvia>13968481-8</RutEnvia>
<RutReceptor>60803000-K</RutReceptor>
<FchResol>2016-04-25</FchResol>
<NroResol>0</NroResol>
<TmstFirmaEnv>2016-09-27T09:22:21</TmstFirmaEnv>
<SubTotDTE>
<TpoDTE>33</TpoDTE>
<NroDTE>1</NroDTE>
</SubTotDTE>
</Caratula>
<!-- DTE TIMBRADO Y FIRMADO -->
</SetDTE>
<dsig:Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<dsig:SignedInfo>
<dsig:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
<dsig:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<dsig:Reference URI="#SetDoc">
<dsig:Transforms>
<dsig:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
</dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<dsig:DigestValue />
</dsig:Reference>
</dsig:SignedInfo>
<dsig:SignatureValue />
<dsig:KeyInfo>
<dsig:KeyValue />
<dsig:X509Data>
<dsig:X509Certificate />
</dsig:X509Data>
</dsig:KeyInfo>
</dsig:Signature>
</envio:EnvioDTE>
A continuación colocamos el siguiente comando:
xmlsec1 --sign --output EnvioDTEfirmado.xml --privkey-pem privatekey.pem,certprueba.pem --id-attr:ID SetDTE --node-xpath /envio:EnvioDTE/dsig:Signature EnvioDTE.xml
📌 ¿Por qué añadimos manualmente los nodos de firma <Signature>
o <dsig:Signature>
?
Es importante mencionar que xmlsec1 no inserta automáticamente el nodo <Signature>
en la ubicación exacta requerida por el Servicio de Impuestos Internos (SII). En algunos casos, si no existe el nodo en el lugar correcto, simplemente no lo agrega, o lo deja fuera del contexto esperado, lo que genera rechazo al validar el archivo.
Por eso, es necesario preparar previamente el XML e incluir el nodo de firma vacío en el lugar correspondiente. Por ejemplo:
-
En un DTE individual, el nodo
<Signature>
debe ir al final del nodo<Documento>
. -
En un EnvioDTE, el nodo
<dsig:Signature>
debe ir justo después del nodo<SetDTE>
y antes de cerrar<EnvioDTE>
.
Esto se hace así para asegurar que la firma quede en la estructura que el SII espera al validar los archivos.
👀 ¿Por qué uno se llama <Signature>
y el otro <dsig:Signature>
?
Ambos nodos pertenecen al mismo espacio de nombres http://www.w3.org/2000/09/xmldsig#
. La diferencia está en el uso del prefijo:
-
En el DTE se permite usar
<Signature>
sin prefijo, ya que el namespace puede heredarse o estar declarado implícitamente. -
En el EnvioDTE se recomienda usar
<dsig:Signature>
con su prefijo, para evitar conflictos y porque el validador del SII lo busca explícitamente así.
En resumen, añadir estos nodos manualmente asegura que la firma se inserte correctamente y el documento sea válido ante el SII.
Integración en otros lenguajes
Además de la utilidad de línea de comandos, existen librerías y extensiones de xmlsec para diversos lenguajes como Python, Ruby, Delphi y C, que permiten integrar la firma digital directamente en aplicaciones, facilitando la automatización y el procesamiento de XML firmados. --
Comentarios
Publicar un comentario