mx.bigdata.cfdi.CFDv3.java Source code

Java tutorial

Introduction

Here is the source code for mx.bigdata.cfdi.CFDv3.java

Source

/*
 *  Copyright 2010 BigData.mx
 *
 *  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 mx.bigdata.cfdi;

import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.OutputStream;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.List;

import javax.xml.XMLConstants;
import javax.xml.bind.*;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.util.JAXBSource;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Schema;
import javax.xml.validation.Validator;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.ErrorHandler;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;

import mx.bigdata.cfdi.schema.Comprobante;
import mx.bigdata.cfdi.schema.Comprobante.Complemento;
import mx.bigdata.cfdi.schema.ObjectFactory;
import mx.bigdata.cfdi.schema.TimbreFiscalDigital;
import mx.bigdata.cfdi.security.KeyLoader;

public final class CFDv3 {

    private static final String XSLT = "/xslt/cadenaoriginal_3_0.xslt";

    private static final String XSD = "/xsd/cfdv3.xsd";

    private static final JAXBContext CONTEXT = createContext();

    private static final JAXBContext createContext() {
        try {
            return JAXBContext.newInstance("mx.bigdata.cfdi.schema");
        } catch (Exception e) {
            throw new Error(e);
        }
    }

    final Comprobante document;

    public CFDv3(InputStream in) throws Exception {
        this.document = load(in);
    }

    public CFDv3(Comprobante comprobante) throws Exception {
        this.document = copy(comprobante);
    }

    private TransformerFactory tf;

    public void setTransformerFactory(TransformerFactory tf) {
        this.tf = tf;
    }

    public void sign(PrivateKey key, Certificate cert) throws Exception {
        String signature = getSignature(key);
        document.setSello(signature);
        byte[] bytes = cert.getEncoded();
        Base64 b64 = new Base64(-1);
        String certStr = b64.encodeToString(bytes);
        document.setCertificado(certStr);
    }

    public void validate() throws Exception {
        validate(null);
    }

    public void validate(ErrorHandler handler) throws Exception {
        SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        Schema schema = sf.newSchema(getClass().getResource(XSD));
        Validator validator = schema.newValidator();
        if (handler != null) {
            validator.setErrorHandler(handler);
        }
        validator.validate(new JAXBSource(CONTEXT, document));
    }

    public void verify() throws Exception {
        String certStr = document.getCertificado();
        Base64 b64 = new Base64();
        byte[] cbs = b64.decode(certStr);
        X509Certificate cert = KeyLoader.loadX509Certificate(new ByteArrayInputStream(cbs));
        cert.checkValidity();
        String sigStr = document.getSello();
        byte[] signature = b64.decode(sigStr);
        byte[] bytes = getOriginalBytes();
        Signature sig = Signature.getInstance("SHA1withRSA");
        sig.initVerify(cert);
        sig.update(bytes);
        boolean bool = sig.verify(signature);
        if (!bool) {
            throw new Exception("Invalid signature");
        }
    }

    public void marshal(OutputStream out) throws Exception {
        Marshaller m = CONTEXT.createMarshaller();
        m.setProperty("com.sun.xml.bind.namespacePrefixMapper", new NamespacePrefixMapperImpl());
        m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        m.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, "http://www.sat.gob.mx/cfd/3 cfdv3.xsd");
        m.marshal(document, out);
    }

    byte[] getOriginalBytes() throws Exception {
        JAXBSource in = new JAXBSource(CONTEXT, document);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        Result out = new StreamResult(baos);
        TransformerFactory factory = tf;
        if (factory == null) {
            factory = TransformerFactory.newInstance();
        }
        Transformer transformer = factory.newTransformer(new StreamSource(getClass().getResourceAsStream(XSLT)));
        transformer.transform(in, out);
        return baos.toByteArray();
    }

    String getOriginalString() throws Exception {
        byte[] bytes = getOriginalBytes();
        return new String(bytes);
    }

    byte[] getDigest() throws Exception {
        byte[] bytes = getOriginalBytes();
        return DigestUtils.sha(bytes);
    }

    String getSignature(PrivateKey key) throws Exception {
        byte[] bytes = getOriginalBytes();
        Signature sig = Signature.getInstance("SHA1withRSA");
        sig.initSign(key);
        sig.update(bytes);
        byte[] signed = sig.sign();
        Base64 b64 = new Base64(-1);
        return b64.encodeToString(signed);
    }

    Comprobante getComprobante() throws Exception {
        return copy(document);
    }

    // Defensive deep-copy
    private Comprobante copy(Comprobante comprobante) throws Exception {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document doc = db.newDocument();
        Marshaller m = CONTEXT.createMarshaller();
        m.marshal(comprobante, doc);
        Unmarshaller u = CONTEXT.createUnmarshaller();
        return (Comprobante) u.unmarshal(doc);
    }

    private Comprobante load(InputStream source) throws Exception {
        try {
            Unmarshaller u = CONTEXT.createUnmarshaller();
            return (Comprobante) u.unmarshal(source);
        } finally {
            source.close();
        }
    }

    public static void dump(String title, byte[] bytes, PrintStream out) {
        out.printf("%s: ", title);
        for (byte b : bytes) {
            out.printf("%02x ", b & 0xff);
        }
        out.println();
    }
}