/** * Copyright [2009] [NIC Labs] * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or * agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. * **/ package cl.nic.dte.extension; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.security.InvalidAlgorithmParameterException; import java.security.KeyException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.Date; import java.util.HashMap; import javax.xml.crypto.MarshalException; import javax.xml.crypto.dsig.XMLSignature; import javax.xml.crypto.dsig.XMLSignatureException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.xmlbeans.XmlDateTime; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlOptions; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import cl.nic.dte.VerifyResult; import cl.nic.dte.util.Utilities; import cl.nic.dte.util.XMLUtil; import cl.sii.siiDte.libroboletas.LibroBoletaDocument; import cl.sii.siiDte.libroboletas.SignatureType; import cl.sii.siiDte.libroboletas.LibroBoletaDocument.LibroBoleta.EnvioLibro.TmstFirma; public class LibroBoletaDocumentExtensionHandler { public static byte[] getBytes(LibroBoletaDocument dte) throws IOException { XmlOptions opts = new XmlOptions(); opts.setCharacterEncoding("ISO-8859-1"); ByteArrayOutputStream out = new ByteArrayOutputStream(); dte.save(out, opts); return out.toByteArray(); } public static VerifyResult verifyXML(LibroBoletaDocument dte) { return XMLUtil.verifyXML(dte); } public static VerifyResult verifySignature(LibroBoletaDocument dte) { // Ojo, verifica la firma usando el keyValue, pero no verifica que el // value corresponda al certificado incluido, ni la validez del // certificado. SignatureType sign = dte.getLibroBoleta().getSignature(); XmlOptions opts = new XmlOptions(); opts.setCharacterEncoding("ISO-8859-1"); opts.setSaveOuter(); // Debido a que SII define Signature en una estructura interna, estoy // obligado a cambiar el namespace y a hacer cosas raras/feas HashMap<String, String> namespaces = new HashMap<String, String>(); namespaces.put("http://www.sii.cl/SiiDte", "http://www.w3.org/2000/09/xmldsig#"); opts.setLoadSubstituteNamespaces(namespaces); // Find Signature element if (sign == null || sign.isNil()) { return new VerifyResult(VerifyResult.XML_SIGNATURE_WRONG, false, Utilities.verificationLabels .getString("XML_SIGNATURE_ERROR_NOTFOUND")); } try { cl.sii.siiDte.dsig.SignatureType sign2 = cl.sii.siiDte.dsig.SignatureType.Factory .parse(sign.newInputStream(opts), opts); // No quiero afectar el dte original, saco copia LibroBoletaDocument dte2 = (LibroBoletaDocument) dte.copy(); dte2.getLibroBoleta().getSignature().set(sign2); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); DocumentBuilder builder = dbf.newDocumentBuilder(); org.w3c.dom.Document doc = builder.parse(dte2.newInputStream()); NodeList nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature"); if (nl.getLength() == 0) { return new VerifyResult(VerifyResult.XML_SIGNATURE_WRONG, false, Utilities.verificationLabels .getString("XML_SIGNATURE_ERROR_NOTFOUND")); } return (XMLUtil.verifySignature(nl.item(0), dte.getLibroBoleta() .getEnvioLibro().getTmstFirma().getTime())); } catch (XmlException e) { return (new VerifyResult(VerifyResult.XML_SIGNATURE_WRONG, false, Utilities.verificationLabels .getString("XML_SIGNATURE_ERROR_UNKNOWN") + ": " + e.getMessage())); } catch (IOException e) { return (new VerifyResult(VerifyResult.XML_SIGNATURE_WRONG, false, Utilities.verificationLabels .getString("XML_SIGNATURE_ERROR_UNKNOWN") + ": " + e.getMessage())); } catch (ParserConfigurationException e) { return (new VerifyResult(VerifyResult.XML_SIGNATURE_WRONG, false, Utilities.verificationLabels .getString("XML_SIGNATURE_ERROR_UNKNOWN") + ": " + e.getMessage())); } catch (SAXException e) { return (new VerifyResult(VerifyResult.XML_SIGNATURE_WRONG, false, Utilities.verificationLabels .getString("XML_SIGNATURE_ERROR_UNKNOWN") + ": " + e.getMessage())); } } public static byte[] sign(LibroBoletaDocument dte, PrivateKey pKey, X509Certificate cert) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, KeyException, MarshalException, XMLSignatureException, SAXException, IOException, ParserConfigurationException, XmlException { String uri = "#"+dte.getLibroBoleta().getEnvioLibro().getID(); // Segun el esquema no estoy obligado a este formato de fecha, pero // prefiero TmstFirma fr = TmstFirma.Factory.newInstance(); fr.set(XmlDateTime.Factory.newValue(Utilities.fechaHoraFormat .format(new Date()))); dte.getLibroBoleta().getEnvioLibro().xsetTmstFirma(fr); XMLUtil.signEmbeded(dte.getLibroBoleta().getDomNode(), uri, pKey, cert); HashMap<String, String> namespaces = new HashMap<String, String>(); namespaces.put("http://www.w3.org/2000/09/xmldsig#", "http://www.sii.cl/SiiDte"); XmlOptions opts = new XmlOptions(); opts.setCharacterEncoding("ISO-8859-1"); // Debido a que SII define Signature en una estructura interna, estoy // obligado a cambiar el namespace opts.setLoadSubstituteNamespaces(namespaces); dte.set(LibroBoletaDocument.Factory.parse(dte.newInputStream(), opts)); ByteArrayOutputStream out = new ByteArrayOutputStream(); dte.save(out, opts); return out.toByteArray(); } public static X509Certificate getCertificate(LibroBoletaDocument dte) { return XMLUtil.getCertificate(dte.getLibroBoleta().getSignature()); } }