Firma de un Documento XML con Certificado Digital en Java para Uso Tributario en Chile
En este tutorial, aprenderemos a firmar un documento XML utilizando Java y un certificado digital, con un enfoque particular en aplicaciones tributarias como la facturación electrónica en Chile. Este procedimiento asegura la autenticidad e integridad del documento firmado.
Requisitos previos:
- Poseer un certificado digital en formato .pfx o .p12.
- Tener conocimientos básicos de Java.
- Estar familiarizado con conceptos de criptografía y firma digital.
Este tutorial fue desarrollado de manera práctica, mediante prueba y error, y complementado con una cuidadosa búsqueda de información en diversas fuentes, entre ellas la documentación oficial de Oracle. El objetivo es proporcionar una solución funcional para firmar documentos XML con certificados digitales en Java, especialmente útil en el contexto tributario chileno.
A diferencia de enfoques previos que dependían de bibliotecas externas como Apache Santuario, en este ejemplo utilizaremos únicamente las clases estándar de Java (disponibles desde Java 7).
Anteriormente, realizaba este proceso en PHP utilizando herramientas como xmlsec y OpenSSL, que facilitaban la firma de documentos XML. Sin embargo, decidí migrar a Java debido a su robusta y completa biblioteca criptográfica, que simplifica la implementación de soluciones avanzadas. Un lenguaje con características similares en este ámbito es C#, gracias a su extensa integración de herramientas criptográficas en el framework .NET.
Xml Requerido
Se asume que tenemos es el siguiente documento xml a firmar con la siguiente estructura. El nombre del archivo es xmlentrante.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<DTE version="1.0">
<Documento ID="F27T33">
<Encabezado>
<IdDoc>
<TipoDTE>33</TipoDTE>
<Folio>27</Folio>
<FchEmis>2003-09-08</FchEmis>
</IdDoc>
<Emisor>
<RUTEmisor>97975000-5</RUTEmisor>
<RznSoc>RUT DE PRUEBA</RznSoc>
<GiroEmis>Insumos de Computacion</GiroEmis>
<Acteco>31341</Acteco>
<CdgSIISucur>1234</CdgSIISucur>
<DirOrigen>Teatinos 120, Piso 4</DirOrigen>
<CmnaOrigen>Santiago</CmnaOrigen>
<CiudadOrigen>Santiago</CiudadOrigen>
</Emisor>
<Receptor>
<RUTRecep>8414240-9</RUTRecep>
<RznSocRecep>JORGE GONZALEZ LTDA</RznSocRecep>
<GiroRecep>COMPUTACION</GiroRecep>
<DirRecep>SAN DIEGO 2222</DirRecep>
<CmnaRecep>LA FLORIDA</CmnaRecep>
<CiudadRecep>SANTIAGO</CiudadRecep>
</Receptor>
<Totales>
<MntNeto>426226</MntNeto>
<TasaIVA>18</TasaIVA>
<IVA>76720</IVA>
<MntTotal>502946</MntTotal>
</Totales>
</Encabezado>
<Detalle>
<NroLinDet>1</NroLinDet>
<CdgItem>
<TpoCodigo>INT1</TpoCodigo>
<VlrCodigo>011</VlrCodigo>
</CdgItem>
<NmbItem>Cajon AFECTO</NmbItem>
<DscItem/>
<QtyItem>139</QtyItem>
<PrcItem>1807</PrcItem>
<MontoItem>251173</MontoItem>
</Detalle>
<Detalle>
<NroLinDet>2</NroLinDet>
<CdgItem>
<TpoCodigo>INT1</TpoCodigo>
<VlrCodigo>022</VlrCodigo>
</CdgItem>
<NmbItem>Relleno AFECTO</NmbItem>
<DscItem/>
<QtyItem>59</QtyItem>
<PrcItem>2967</PrcItem>
<MontoItem>175053</MontoItem>
</Detalle>
<Referencia>
<NroLinRef>1</NroLinRef>
<TpoDocRef>SET</TpoDocRef>
<FolioRef>1</FolioRef>
<FchRef>2003-08-01</FchRef>
<CodRef>1</CodRef>
<RazonRef>Caso 4256-1</RazonRef>
</Referencia>
<TED version="1.0">
<DD>
<RE>97975000-5</RE>
<TD>33</TD>
<F>27</F>
<FE>2003-09-08</FE>
<RR>8414240-9</RR>
<RSR>JORGE GONZALEZ LTDA</RSR>
<MNT>502946</MNT>
<IT1>Cajon AFECTO</IT1>
<CAF version="1.0">
<DA>
<RE>97975000-5</RE>
<RS>RUT DE PRUEBA</RS>
<TD>33</TD>
<RNG>
<D>1</D>
<H>200</H>
</RNG>
<FA>2003-09-04</FA>
<RSAPK>
<M>0a4O6Kbx8Qj3K4iWSP4w7KneZYeJ+g/prihYtIEolKt3cykSxl1zO8vSXu397QhTmsX7SBEudTUx++2zDXBhZw==</M>
<E>Aw==</E>
</RSAPK>
<IDK>100</IDK>
</DA>
<FRMA
algoritmo="SHA1withRSA">g1AQX0sy8NJugX52k2hTJEZAE9Cuul6pqYBdFxj1N17umW7zG/hAavCALKByHzdYAfZ3LhGTXCai5zNxOo4lDQ==</FRMA>
</CAF>
<TSTED>2003-09-08T12:28:31</TSTED>
</DD>
<FRMT
algoritmo="SHA1withRSA">pqjXHHQLJmyFPMRvxScN7tYHvIsty0pqL2LLYaG43jMmnfiZfllLA0wb32lP+HBJ/tf8nziSeorvjlx410ZImw==</FRMT>
</TED>
<TmstFirma>2003-09-08T12:28:31</TmstFirma>
</Documento>
</DTE>
Xml resultante
El archivo resultante despues de aplicar la firma se llama xmlsaliente.xml y con la firma aplicada debería tener la siguente estructura:
<?xml version="1.0" encoding="ISO-8859-1"?> <DTE version="1.0"> <Documento ID="F27T33"> <Encabezado> <IdDoc> <TipoDTE>33</TipoDTE> <Folio>27</Folio> <FchEmis>2003-09-08</FchEmis> </IdDoc> <Emisor> <RUTEmisor>97975000-5</RUTEmisor> <RznSoc>RUT DE PRUEBA</RznSoc> <GiroEmis>Insumos de Computacion</GiroEmis> <Acteco>31341</Acteco> <CdgSIISucur>1234</CdgSIISucur> <DirOrigen>Teatinos 120, Piso 4</DirOrigen> <CmnaOrigen>Santiago</CmnaOrigen> <CiudadOrigen>Santiago</CiudadOrigen> </Emisor> <Receptor> <RUTRecep>8414240-9</RUTRecep> <RznSocRecep>JORGE GONZALEZ LTDA</RznSocRecep> <GiroRecep>COMPUTACION</GiroRecep> <DirRecep>SAN DIEGO 2222</DirRecep> <CmnaRecep>LA FLORIDA</CmnaRecep> <CiudadRecep>SANTIAGO</CiudadRecep> </Receptor> <Totales> <MntNeto>426226</MntNeto> <TasaIVA>18</TasaIVA> <IVA>76720</IVA> <MntTotal>502946</MntTotal> </Totales> </Encabezado> <Detalle> <NroLinDet>1</NroLinDet> <CdgItem> <TpoCodigo>INT1</TpoCodigo> <VlrCodigo>011</VlrCodigo> </CdgItem> <NmbItem>Cajon AFECTO</NmbItem> <DscItem/> <QtyItem>139</QtyItem> <PrcItem>1807</PrcItem> <MontoItem>251173</MontoItem> </Detalle> <Detalle> <NroLinDet>2</NroLinDet> <CdgItem> <TpoCodigo>INT1</TpoCodigo> <VlrCodigo>022</VlrCodigo> </CdgItem> <NmbItem>Relleno AFECTO</NmbItem> <DscItem/> <QtyItem>59</QtyItem> <PrcItem>2967</PrcItem> <MontoItem>175053</MontoItem> </Detalle> <Referencia> <NroLinRef>1</NroLinRef> <TpoDocRef>SET</TpoDocRef> <FolioRef>1</FolioRef> <FchRef>2003-08-01</FchRef> <CodRef>1</CodRef> <RazonRef>Caso 4256-1</RazonRef> </Referencia> <TED version="1.0"> <DD> <RE>97975000-5</RE> <TD>33</TD> <F>27</F> <FE>2003-09-08</FE> <RR>8414240-9</RR> <RSR>JORGE GONZALEZ LTDA</RSR> <MNT>502946</MNT> <IT1>Cajon AFECTO</IT1> <CAF version="1.0"> <DA> <RE>97975000-5</RE> <RS>RUT DE PRUEBA</RS> <TD>33</TD> <RNG> <D>1</D> <H>200</H> </RNG> <FA>2003-09-04</FA> <RSAPK> <M>0a4O6Kbx8Qj3K4iWSP4w7KneZYeJ+g/prihYtIEolKt3cykSxl1zO8vSXu397QhTmsX7SBEudTUx++2zDXBhZw==</M> <E>Aw==</E> </RSAPK> <IDK>100</IDK> </DA> <FRMA algoritmo="SHA1withRSA">g1AQX0sy8NJugX52k2hTJEZAE9Cuul6pqYBdFxj1N17umW7zG/hAavCALKByHzdYAfZ3LhGTXCai5zNxOo4lDQ==</FRMA> </CAF> <TSTED>2003-09-08T12:28:31</TSTED> </DD> <FRMT algoritmo="SHA1withRSA">pqjXHHQLJmyFPMRvxScN7tYHvIsty0pqL2LLYaG43jMmnfiZfllLA0wb32lP+HBJ/tf8nziSeorvjlx410ZImw==</FRMT> </TED> <TmstFirma>2003-09-08T12:28:31</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="#F27T33"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><DigestValue>K+pkDVlAlNDB9QI1pdQ7QJWEKXY=</DigestValue></Reference></SignedInfo><SignatureValue>rQEW0vtBYjImVaK2Nh3vhNJ3hccBlYIc1/X1z1ZW8JyqycEciJ27sF72CjMtlCuVIO6yqsc2/Zcn Nm2bES04GQZ/L+v/QVzsuHgoUk+QzCte9ZUEEUZGibgd7c9ojiZ6Qsvuq3u0rmymegDX+oNNR61X 6gqS2qmAfWVlyhn9Inz67Paq/wxAfrGKUnK41XQZfAeEZRYCafaoukZsKNkCApiCdK9qc6fAMYrE X1i/60fB0cr8NWVyGvphRWGTpgRsvbfC10gN2GZdfXT0AMB96uquiEXJyf+JWJEnv4SB1yQX0cEz hoXdAb8UcqDF5ru85jZ4UTqXVp0Opb/g4IENfA==</SignatureValue><KeyInfo><KeyValue><RSAKeyValue><Modulus>r6/DT57udLenXPgLNqiqjKCD3X8b7b5rqUW3n4hKoaBnD04vf8csiaSSzTx7yPHCWyl4ueyij5mu J3OZuxWScKM0l3LYi/0ia9jXpBtGahIzHEyyMb6jh3O+KaZdyqxu6f+l2VRp1aV9Vxf3yRUxXFgt JdbmziVZ7or6RjFx9hdAFxasljvrwpqlS03XtuymaURkUhuL4bQsPp5TnRHYDl/uQpeH0NlTQRrv 2QR4/tbJh9mcfL68EncrtuB6x2LOv4YbPihSKlNO7qnFNXX/3oDA1zaVaw+cnjqShpN01CGDsK7a HsXmv/KxWRBQCHEPXc9wWnd5tABaCiOmSSjRWQ==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue></KeyValue><X509Data><X509Certificate>MIIHvjCCBaagAwIBAgIKLf7spgABAArbWjANBgkqhkiG9w0BAQsFADCBvzELMAkGA1UEBhMCQ0wx HTAbBgNVBAgTFFJlZ2lvbiBNZXRyb3BvbGl0YW5hMREwDwYDVQQHEwhTYW50aWFnbzEUMBIGA1UE ChMLRS1DRVJUQ0hJTEUxIDAeBgNVBAsTF0F1dG9yaWRhZCBDZXJ0aWZpY2Fkb3JhMR4wHAYDVQQD ExVFLUNFUlRDSElMRSBDQSBGRVMgMDIxJjAkBgkqhkiG9w0BCQEWF3NjbGllbnRlQGUtY2VydGNo aWxlLmNsMB4XDTI0MDcxMTIwMjYxNloXDTI1MDcxMTIwMjYxNlowgboxCzAJBgNVBAYTAkNMMRIw EAYDVQQIEwlMT1MgTEFHT1MxEzARBgNVBAcTCkxMQU5RVUlIVUUxKTAnBgNVBAoTIEVTVEVCQU4g R0FCUklFTCBHVUVOVUwgQUxNT05BQ0lEMQowCAYDVQQLDAEqMSkwJwYDVQQDEyBFU1RFQkFOIEdB QlJJRUwgR1VFTlVMIEFMTU9OQUNJRDEgMB4GCSqGSIb3DQEJARYRRUdVRU5VTEBBTVVMRU4uQ0ww ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvr8NPnu50t6dc+As2qKqMoIPdfxvtvmup RbefiEqhoGcPTi9/xyyJpJLNPHvI8cJbKXi57KKPma4nc5m7FZJwozSXctiL/SJr2NekG0ZqEjMc TLIxvqOHc74ppl3KrG7p/6XZVGnVpX1XF/fJFTFcWC0l1ubOJVnuivpGMXH2F0AXFqyWO+vCmqVL Tde27KZpRGRSG4vhtCw+nlOdEdgOX+5Cl4fQ2VNBGu/ZBHj+1smH2Zx8vrwSdyu24HrHYs6/hhs+ KFIqU07uqcU1df/egMDXNpVrD5yeOpKGk3TUIYOwrtoexea/8rFZEFAIcQ9dz3Bad3m0AFoKI6ZJ KNFZAgMBAAGjggK9MIICuTCCAV8GA1UdIASCAVYwggFSMIIBTgYIKwYBBAHDUgUwggFAMD0GCCsG AQUFBwIBFjFodHRwczovL3d3dy5lLWNlcnRjaGlsZS5jbC9wb2xpdGljYXMteS1wcmFjdGljYXMv MIH+BggrBgEFBQcCAjCB8R6B7gBFAGwAIAByAGUAcwBwAG8AbgBkAGUAcgAgAGUAcwB0AGUAIABm AG8AcgBtAHUAbABhAHIAaQBvACAAZQBzACAAdQBuACAAcgBlAHEAdQBpAHMAaQB0AG8AIABpAG4A ZABpAHMAcABlAG4AcwBhAGIAbABlACAAcABhAHIAYQAgAGQAYQByACAAaQBuAGkAYwBpAG8AIABh AGwAIABwAHIAbwBjAGUAcwBvACAAZABlACAAYwBlAHIAdABpAGYAaQBjAGEAYwBpAPMAbgAuACAA UABvAHMAdABlAHIAaQBvAHIAbQBlAG4AdABlACwwHQYDVR0OBBYEFEIQxwqlOHKzTW0Ke3GQD5IG SkFUMAsGA1UdDwQEAwIE8DAjBgNVHREEHDAaoBgGCCsGAQQBwQEBoAwWCjEzOTY4NDgxLTgwHwYD VR0jBBgwFoAUdNYhs/Ra6C18u1kGY0PvabQ6kwQwQAYDVR0fBDkwNzA1oDOgMYYvaHR0cDovL2Ny bC5lY2VydGNoaWxlLmNsL0UtQ0VSVENISUxFQ0FGRVMwMi5jcmwwPQYIKwYBBQUHAQEEMTAvMC0G CCsGAQUFBzABhiFodHRwOi8vb2NzcGZlcy5lY2VydGNoaWxlLmNsL29jc3AwPAYJKwYBBAGCNxUH BC8wLQYlKwYBBAGCNxUIgd3STZfUOoaJnQaHiM5vg6/MYEWFpL44gp6fbQIBZAIBAjAjBgNVHRIE HDAaoBgGCCsGAQQBwQECoAwWCjk2OTI4MTgwLTUwDQYJKoZIhvcNAQELBQADggIBACSz9zG02Aak gi1QLePfOQH3vBkjrSqmsLtz6qTR11EyUjNWCy6amat2FJ1fLrcAtOD1FrO3167riBRESBfrJ+br Ug9Dli1zortTUi6hVBrGC9TnKtXCX4hfhnPGwA+tlIEKS9fSYYNGw4R8LYGYs0BSZFrsizKU9kP4 2RvyEwh4KfYLm6wWYZN7Hm2eFZ7pIbRh5fILgmK47vRpfbY1nrj3tBv+INQjO71B6dn/khYuZpAb bNwwss/mcJWPzZnuS6aCIu5Vq1lO1ZkuIU6gvIQdRWE3IiiMW3EO12C4ZGasol87SPDDVKdhmWqZ 97LeUn5Kh6KYrzFVHkQVK0BFxsqoobo3++eDwF13y1hcFkOlFIJs2z7dOmdBRpQ3KFJo/zsW4K+8 K7f6SEPJbCRgeJWKtv3dzx1yI+JDa3ls5LGfVjL/MXz+f9/CuT8I5a8EVChwQIxrCTtnfHsYSeh0 0091meVCGdx2o/QLUycWPBbH9jAU07gsOAnmi27egJ3KgfcVq9b4zzGeZRro64TFnaTN9taEARBy Hu+2eNvpD/WZTqvQgtTeEDEPKbL5Sl++dIiFnULmB1bGBuFaMMmIVtx8fm8hvdYgjIPo53rZHy/R TBvycBrmauF89daVele0LiMgU3a9aoY1QrvIIvUx8sqdBYxSZra6R9ftoELcCquo</X509Certificate></X509Data></KeyInfo></Signature></DTE>
Código
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Main.java to edit this template
*/
package ejemplofirma;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableEntryException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import javax.xml.crypto.MarshalException;
import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.crypto.dsig.DigestMethod;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.SignatureMethod;
import javax.xml.crypto.dsig.SignedInfo;
import javax.xml.crypto.dsig.Transform;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureException;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
import javax.xml.crypto.dsig.keyinfo.KeyValue;
import javax.xml.crypto.dsig.keyinfo.X509Data;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
public class EjemploFirma {
public static void main(String args[]) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, KeyStoreException, FileNotFoundException, IOException, UnrecoverableEntryException, CertificateException, KeyException, ParserConfigurationException, SAXException, MarshalException, XMLSignatureException, TransformerConfigurationException, TransformerException{
String pathdte = "/home/esteban/appdte/DTE/";
String nombredte = "xmlentrante.xml";
String pathcertificado = "/home/esteban/appdte/certificate/eguenul.pfx";
String xml_saliente = "xmlsaliente.xml";
String atributo_id = "F27T33";
String clave = "amulen1956";
String nodo_xml = "Documento";
/* CREO LOS ELEMENTOS DE FIRMA */
// Create a DOM XMLSignatureFactory that will be used to
// generate the enveloped signature.
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
// Create a Reference to the enveloped document (in this case,
// you are signing the whole document, so a URI of "" signifies
// that, and also specify the SHA1 digest algorithm and
// the ENVELOPED Transform.
Reference ref = fac.newReference
("#"+atributo_id, fac.newDigestMethod(DigestMethod.SHA1, null),
Collections.singletonList
(fac.newTransform
(Transform.ENVELOPED, (TransformParameterSpec) null)),
null, null);
// Create the SignedInfo.
SignedInfo si = fac.newSignedInfo
(fac.newCanonicalizationMethod
(CanonicalizationMethod.INCLUSIVE,
(C14NMethodParameterSpec) null),
fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null),
Collections.singletonList(ref));
/* instancio el certificado digital */
KeyStore p12 = KeyStore.getInstance("pkcs12");
p12.load(new FileInputStream(pathcertificado), clave.trim().toCharArray());
Enumeration e = p12.aliases();
String alias = (String) e.nextElement();
System.out.println("Alias certifikata:" + alias);
KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry) p12.getEntry(alias, new KeyStore.PasswordProtection(clave.trim().toCharArray()));
X509Certificate cert = (X509Certificate) keyEntry.getCertificate();
// Create the KeyInfo containing the X509Data.
KeyInfoFactory kif = fac.getKeyInfoFactory();
List x509Content = new ArrayList();
x509Content.add(cert);
X509Data xd = kif.newX509Data(x509Content);
KeyValue keyValue = kif.newKeyValue(cert.getPublicKey());
ArrayList item = new ArrayList();
item.add(keyValue);
item.add(xd);
/*
KeyInfo ki = kif.newKeyInfo(Collections.singletonList(xd));
*/
KeyInfo ki = kif.newKeyInfo(item);
/* INSTANCIO EL DOCUMENTO A FIRMAR */
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder().parse
(new FileInputStream(pathdte+nombredte));
/* NODO XML QUE VOY A COLOCAR LA FIRMA */
Node documento = doc.getElementsByTagName(nodo_xml).item(0);
Element eldocumento =(Element) documento;
eldocumento.setIdAttribute("ID", true);
// Create a DOMSignContext and specify the RSA PrivateKey and
// location of the resulting XMLSignature's parent element.
DOMSignContext dsc = new DOMSignContext
(keyEntry.getPrivateKey(), doc.getDocumentElement());
// Create the XMLSignature, but don't sign it yet.
XMLSignature signature = fac.newXMLSignature(si, ki);
// Marshal, generate, and sign the enveloped signature.
signature.sign(dsc);
/* CON EL DOCUMENTO YA FIRMADO REDIRIJO LA SALIDA HACIA UN ARCHIVO RESULTANTE */
ByteArrayOutputStream os2;
os2 = new ByteArrayOutputStream();
TransformerFactory tf = TransformerFactory.newInstance();
Transformer trans = tf.newTransformer();
trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
trans.setOutputProperty(OutputKeys.ENCODING, "ISO-8859-1");
trans.setOutputProperty(OutputKeys.INDENT, "no");
String xmlDecl = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>"
+ System.getProperty("line.separator");
os2.write(xmlDecl.getBytes("ISO-8859-1"));
trans.transform(new DOMSource(doc), new StreamResult(os2));
FileOutputStream fos = new FileOutputStream(new File(pathdte+xml_saliente));
os2.writeTo(fos);
byteXML objByteXML = new byteXML();
objByteXML.cleanXML(pathdte+xml_saliente);
}
}
class byteXML {
public void cleanXML(String filePath) throws IOException{
System.setProperty("file.encoding", "ISO-8859-1");
byte[] bytes = Files.readAllBytes(Paths.get(filePath));
String strDATA = new String(bytes, "ISO-8859-1");
String strCleaned = strDATA.replaceAll(" ", "");
byte[] bytes2 = strCleaned.getBytes("ISO-8859-1");
try (OutputStream os = new FileOutputStream(filePath)) {
os.write(bytes2);
}
}
}
Creación de la firma XML:
- Se configura una firma
XMLSignature
utilizando el algoritmo RSA con SHA1, con el documento a firmar como referencia. - Se utiliza una referencia al documento XML que se firma, especificando el algoritmo de hash y el método de transformación (ENVELOPED).
- Se configura una firma
Carga del certificado:
- El certificado digital en formato PKCS#12 (
.p12
) es cargado desde un archivo especificado por el usuario. - Se extrae el alias del certificado, y luego se obtiene la clave privada y el certificado X.509 correspondiente.
- El certificado digital en formato PKCS#12 (
Preparación del documento XML a firmar:
- Se carga el documento XML a firmar (
DTE
). - Se selecciona el nodo XML correspondiente, en este caso, el nodo que se pasará como parámetro al método.
- Este nodo se marca con un atributo
ID
necesario para la firma.
- Se carga el documento XML a firmar (
Configuración de la firma digital:
- Se configura el contexto de la firma (
DOMSignContext
) utilizando la clave privada obtenida del certificado. - La firma se realiza utilizando el contexto y se genera el XML firmado.
- Se configura el contexto de la firma (
Transformación y salida del XML firmado:
- Después de firmar el documento, se transforma y se escribe en un archivo de salida.
- Se utiliza un
Transformer
para eliminar la declaración XML y para establecer la codificación y la indentación.
Limpieza del XML:
- Después de generar el archivo XML firmado, el método
cleanXML
se encarga de eliminar caracteres indeseados como
, para garantizar que el XML esté limpio.
- Después de generar el archivo XML firmado, el método
- Formato del XML: Asegúrate de que el XML cumple con las especificaciones del Servicio de Impuestos Internos (SII) de Chile.
- Certificado Válido: El certificado utilizado debe estar vigente y emitido por una entidad emisora de certificados tributarios digitales en Chile.
- Validación: Después de la firma, puedes verificar la firma digital utilizando herramientas en línea de validación de firmas.
Conclusión:
Este tutorial demuestra cómo utilizar la API estándar de Java para firmar documentos XML con un certificado digital. Si necesitas agregar funcionalidades adicionales o personalizar la firma para cumplir con requisitos específicos, la flexibilidad de Java te permitirá hacerlo fácilmente.
Sos Grande amigo Esteban y muchas gracias por compartir tus conocimientos
ResponderEliminarEl DTE no utiliza ningún tipo de XSD schema?
ResponderEliminarLa pagina del SII tiene un XSD contra el cual puedes validar tu xml generado.
ResponderEliminar