org.viafirma.nucleo.Nucleo.java Source code

Java tutorial

Introduction

Here is the source code for org.viafirma.nucleo.Nucleo.java

Source

/* Copyright (C) 2007 Flix Garca Borrego (borrego at gmail.com)
      
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.
      
   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.
      
   You should have received a copy of the GNU Library General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
   MA 02111-1307, USA 
 */

package org.viafirma.nucleo;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.mail.MessagingException;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.ParserConfigurationException;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;

import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.lang.time.DateUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.MultiPartEmail;
import org.apache.xml.security.keys.keyresolver.KeyResolverException;
import org.apache.xml.security.signature.XMLSignature;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.viafirma.cliente.firma.TypeFile;
import org.viafirma.cliente.firma.TypeFormatSign;
import org.viafirma.cliente.util.ConfigUtil;
import org.viafirma.cliente.vo.FirmaInfoViafirma;
import org.viafirma.conector.ConectorFirmaRMI;
import org.viafirma.excepciones.CodigoError;
import org.viafirma.excepciones.ExcepcionCertificadoNoEncontrado;
import org.viafirma.excepciones.ExcepcionErrorInterno;
import org.viafirma.nucleo.X509.CertificadoGenericoFactory;
import org.viafirma.nucleo.autenticacion.AutenticacionBridge;
import org.viafirma.nucleo.custodia.Custodia;
import org.viafirma.nucleo.firma.FirmaBridge;
import org.viafirma.nucleo.inicio.SecurityProvidersUtils;
import org.viafirma.nucleo.validacion.ValidadorHandler;
import org.viafirma.util.Constantes;
import org.viafirma.util.FaceletsUtil;
import org.viafirma.util.QRCodeUtil;
import org.viafirma.util.SendMailUtil;
import org.viafirma.util.XmlSignUtil;
import org.viafirma.vo.CertificadoGenerico;
import org.viafirma.vo.Documento;
import org.w3c.dom.Document;
import com.viavansi.framework.core.servlet.StartupServletContextListener;
import com.viavansi.framework.core.util.BeanUtil;
import com.viavansi.framework.core.util.CadenaUtilities;
import com.viavansi.framework.tools.repositorio.vo.DocumentVO;

/**
 * Nucleo de la plataforma de autenticacin y firma. Sus funciones principales
 * son :
 * 
 * <pre>
 *    -Inicializar el sistema. init()
 *  -Iniciar el proceso de autenticacin redireccionando al AutenticacionBridge. iniciarAutenticacion().
 *  -Procesar el resultado de la autenticacin, validando las crls,etc .. finAutenticacion()
 *  -Generar los comprobantes de firma en formato QR Code.
 *  -Delegar en el sistema de Custodia para el almacen de firmas digitales.
 * </pre>
 * 
 * @author Felix Garcia Borrego (borrego at gmail.com)
 * @author Alexis Castilla Armero <Pencerval at Gmail.com>
 */
public class Nucleo extends StartupServletContextListener {

    // logger del nucleo
    private static Log log = LogFactory.getLog(Nucleo.class);

    /**
     * Cache con los certificados gestionados por el Nucleo. Almacena los
     * certificados temporalmente mientras estos se estan procesando
     */
    private Cache cacheCertificados;

    /**
     * Cache con los ficheros que actualmente tiene retenidos el Nucleo a la
     * espera de que sean firmados.
     * 
     */
    private Cache cacheDocumentToSign;

    /**
     * Nombre de la variable en la que se almacena el identificador de
     * autenticacin.
     * 
     */
    public static String SESSION_ID = "sessionID";

    /**
     * El tiempo maximo que el Nucleo mantiene un certificado mientras este es
     * procesado en memoria son 2 minutos.
     */
    private int TIEMPO_VIDA_CERTIFICADO = 2 * 60;

    /**
     * El tiempo maximo que el Nucleo mantiene un documento en espera de que
     * este sea firmado
     */
    private int TIEMPO_VIDA_DOCUMENT_TO_SIGN = 10 * 60;

    /**
     * Identificador de de los certificados que son reconocidos por viafirma
     * Todos los certificados reconocidos por viafirma deben empezar por
     * viafirma_
     */
    public static final String IDENTIFICADOR_CERTIFICADO_VIAFIRMA_KEYSTORE = "viafirma";

    /**
     * Validador de certificados utilizado por el nucleo para determinar si un
     * certificado es vlido o no.
     */
    private ValidadorHandler validador;

    /**
     * Registro rmi en el que se publican los servicios via rmi del ncleo.
     */
    Registry rmiRegistry;

    /**
     * Url pblica para la verificacin de documentos.
     */
    public String URL_PUBLIC_VERIFICATION;

    /**
     * Inicializa el nucleo, y este a su vez inicializa todos los mdulos que
     * dependen de el
     * 
     */
    @Override
    public void init(ServletContext context) {
        singleton = this;
        // 1.- inicializamos el sistema de criptografa
        // Eliminamos el proveedor para evitar que se solapen si ya existia uno.
        //Security.removeProvider(new BouncyCastleProvider().getName());
        //Security.addProvider(new BouncyCastleProvider());
        //log.info("Lista de proveedores disponible:" + Arrays.asList(Security.getProviders()));
        // 1.- Inicializamos los proveedores criptograficos
        SecurityProvidersUtils.initProviders();

        org.apache.xml.security.Init.init();
        // inicializa el sistema de cache para mantener los certificados
        CacheManager manager = CacheManager.getInstance();

        cacheCertificados = new Cache("cacheCertificadosEnUso", 200, false, false, TIEMPO_VIDA_CERTIFICADO,
                TIEMPO_VIDA_CERTIFICADO);
        manager.addCache(cacheCertificados);
        log.debug("Inicializada cache de certificados.");

        cacheDocumentToSign = new Cache("cacheDocumentoToSign", 100, true, false, TIEMPO_VIDA_DOCUMENT_TO_SIGN,
                TIEMPO_VIDA_DOCUMENT_TO_SIGN);
        manager.addCache(cacheDocumentToSign);
        log.debug("Inicializada cache documentos a firmar .");

        // 2.- inicializo el servicio RMI.
        // creamos un registro propio programticamente
        // en lugar de utilizar el registro JNDI del servidor de aplicaciones o
        // el comando rmiregistry para aislar nuestra aplicacin de
        // incompatibilidades
        // entre diferentes servidores de aplicaciones.
        try {
            rmiRegistry = LocateRegistry.createRegistry(Constantes.PORT_RMI);
            // creamos la instancia del Servidor
            ConectorFirmaRMI serverRMI = new ConectorFirmaRMI();
            // publicamos el servidor en el registro
            rmiRegistry.bind(Constantes.NOMBRE_CONECOR_RMI_PUBLICADO, serverRMI);
            log.info("Avtivado registro RMI. Puerto: " + Constantes.PORT_RMI + ", nombre del servicio: "
                    + Constantes.NOMBRE_CONECOR_RMI_PUBLICADO);
            // Publicamos el servicio tambien en web
        } catch (RemoteException e) {
            // No se puede activar el servicio RMI.
            log.fatal("No se puede activar el servicio RMI " + e.getMessage(), e);
        } catch (AlreadyBoundException e) {
            // El puerto ya esta en uso.
            log.fatal("El puesto " + Constantes.PORT_RMI
                    + " ya esta en uso por otra aplicacin. No se puede activar el servicio de firma", e);
        }
        Properties properties = ConfigUtil.getInstance().readConfigPropertes();
        // 3.- iniciamos el sistema de custodia de docuemtos
        Custodia.init(properties);

        // 4.- iniciamos las erramientas de QRCode
        QRCodeUtil.init(properties);

        // 5.- inicializo el envio de Email.
        SendMailUtil.init(properties);

        // Configuracin del sistema de validacin de CRLs. Por defecto la
        // validacin esta activada.
        String tempValidacion = (String) properties.get(Constantes.PARAM_VALIDACION_ONLINE);
        boolean validacionOnline = tempValidacion == null ? true : new Boolean(tempValidacion);
        ValidadorHandler.init(validacionOnline, properties);
        validador = ValidadorHandler.getCurrentInstance();
        log.debug("Inicializado Validador de certificados. Validacin online en: " + validacionOnline);

        // Metemos en el contexto de aplicacin todos los parametros de
        // configuracin
        for (Object key : properties.keySet()) {
            context.setAttribute((String) key, properties.get(key));
        }

        // Recuperamos la url pblica:
        URL_PUBLIC_VERIFICATION = properties.getProperty(Constantes.PARAM_URL_APLICACION)
                + Constantes.PATH_VERIFICACION;

        log.info("Nucleo Inicializado. ");
    }

    /**
     * Recupera una instancia del ncleo. Si el nucleo no se ha inicializado con
     * exito lanza una excepcin
     */
    public static org.viafirma.nucleo.Nucleo getInstance() {
        if (singleton == null) {
            log.fatal(
                    "Ncleo no inicializado correctamente, imposible recuperar la instancia. Revise la secuencia de arranque del contexto");
            throw new IllegalStateException("El ncleo no esta inicializado");
        } else {
            return singleton;
        }
    }

    /**
     * Este motodo es invocado por los conectores cuando desean iniciar el
     * proceso de autenticacin para el usuario que esta haciendo uso del
     * conector.
     * 
     * @param codAplicacion
     *            Cdigo de la aplicacin utilizada( si no se esta utilizando el
     *            protocolo openID)
     * @param tipo
     *            Indica el tipo de autenticacin que va a utilizar el Ncleo(
     *            EJ: openID
     * @throws ExcepcionNoEncontrado
     *             La aplicacin indicada por el conector no existe
     * @throws ExcepcionErrorInterno
     */
    public void iniciarAutenticacion(TipoCliente tipo, HttpServletRequest request, HttpServletResponse response)
            throws ExcepcionErrorInterno {

        // recuperamos al bridge que estamos utilizando segun el tipo de
        // autenticacin a realizar
        Factory factoria = Factory.getInstance();
        AutenticacionBridge autenticacionBridge = factoria.getAuntenticacionBridget(tipo);

        // delegamos el bridget( Handler) para que relize el proceso de
        // autenticacin
        // una vez que este termine de autenticar al usuario retornara invocara
        // al metodo finAutenticacin
        autenticacionBridge.generarSolicitudCertificado(request, response);
    }

    /**
     * Este metodo es invocado por el bridget especifico cuando este ha
     * terminado de realizar la autenticacin. El nucleo deberar validar el
     * certificado, auditar la operacin y retornar los datos del certificado al
     * conector para que este retorne los datos al usuario.
     * 
     * @param certificadoX509
     *            Certificado del usuario
     */
    public void finAutenticacion(String sessionID, HttpServletRequest request, HttpServletResponse response) {
        // recuperamos el certificado
        X509Certificate certificadoX509 = getCertificadoCacheado(sessionID);

        if (certificadoX509 == null) {
            log.info("El certificado no se encuentra en sessin.");
            request.setAttribute("codError", org.viafirma.cliente.exception.CodigoError.ERROR_PROTOCOLO_FIRMA);
            FaceletsUtil.forward(request, response, Constantes.URI_ERROR_FIRMA);
            return;
        }

        // el siguiente paso es validar el certificado con las CRLs.
        CodigoError codigo = validador.validar(certificadoX509); // CodigoError.
        // OK_CERTIFICADO_VALIDADO
        // ;

        if (codigo == CodigoError.OK_CERTIFICADO_VALIDADO) {
            log.info("El certificado ha sido validado, redireccion a la aplicacin llamante");
            try {
                CertificadoGenerico certificado = CertificadoGenericoFactory.getInstance().generar(certificadoX509);
                // invoco al conector Utilizado para que retorne la respuesta al
                // usuario
                // por ahora el conector sera OpenIDServlet
                request.setAttribute("CodigoError", codigo);
                request.setAttribute("certificado", certificado);
                // TODO colocar un getConectorutilizado y utilizar un metoro
                // redirectMe del conector.
                request.getRequestDispatcher("/conectorAutenticacionOpenId").forward(request, response);
                // response.sendRedirect(aplicacion.getUrlRetorno());
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (ServletException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        } else {
            log.info("El certificado no ha sido validado, redireccion a una pagina de error de validacin.");
            request.setAttribute("codError", codigo);
            FaceletsUtil.forward(request, response, Constantes.URI_ERROR_AUTENTICACION);
        }
    }

    // **********************************************
    // Acciones relacionadas con el proceso de Firma
    // **********************************************

    /**
     * Almacena un fichero en la cache de firma y le asigna un identificador. El
     * fichero permanecera unos minutos en la cache a la espera de que el
     * usuario proceda a su firma definitiva
     * 
     * @param documentToSign
     */
    public String prepareSign(Documento documentToSign) {
        // generamos el identificador que tendra el documento.
        String key = generarIdentificador();
        documentToSign.setId(key);
        // almacenamos el documento en la cache
        cacheDocumentToSign.put(new Element(key, documentToSign));
        log.info("Nuevo documento pendiente de firma " + documentToSign);
        return key;
    }

    /**
     * Este motodo es invocado por los conectores cuando desean iniciar el
     * proceso de firma para el usuario que esta haciendo uso del
     * 
     * @param tipo
     *            Indica el tipo de autenticacin que va a utilizar el Ncleo(
     *            EJ: openID
     * @throws ExcepcionNoEncontrado
     *             La aplicacin indicada por el conector no existe
     * @throws ExcepcionErrorInterno
     */
    public void iniciarFirma(TipoCliente tipo, String idFirma, HttpServletRequest request,
            HttpServletResponse response) throws ExcepcionErrorInterno {
        // recuperamos al bridge que estamos utilizando segun el tipo de
        // autenticacin a realizar
        Factory factoria = Factory.getInstance();

        // recupero el documento
        Documento documento = getDocumentoCacheado(idFirma);
        if (documento == null || documento.getDatos() == null) {
            log.warn("El fichero indicado est vaco");
            throw new ExcepcionErrorInterno(CodigoError.ERROR_FILE_EMPTY);
        }

        FirmaBridge firmaBridge = factoria.getFirmaBridget(tipo);
        // delegamos el bridget( Handler) para que relize el proceso de firma
        // una vez que este termine de autenticar al usuario retornara invocara
        // al metodo finAutenticacin
        // TODO controlar excepciones y enviar a pgina de error.
        firmaBridge.generarSolicitudFirma(documento, request, response);
    }

    /**
     * Este metodo es invocado por el bridget especifico cuando este ha
     * terminado de realizar la firma. El nucleo deberar validar el certificado,
     * almacenar el resultado de la firma, auditar la operacin y retornar los
     * datos de la operacin al usuario.
     * 
     * @param certificadoX509
     *            Certificado del usuario
     */
    public void finFirma(String sessionID, String idFirma, HttpServletRequest request,
            HttpServletResponse response) {
        // obtenemos el certificado
        X509Certificate certificadoX509 = getCertificadoCacheado(sessionID);

        if (certificadoX509 == null) {
            log.info("El certificado no se encuentra en sessin.");
            request.setAttribute("codError", org.viafirma.cliente.exception.CodigoError.ERROR_PROTOCOLO_FIRMA);
            FaceletsUtil.forward(request, response, Constantes.URI_ERROR_FIRMA);
            return;
        }

        // obtenemos el documento XML firmado por el usuario
        Document xmlSignedDocument = getXMLDocument(sessionID);

        // Obtenemos el documento original que ha sido firmadofirmado.
        Documento documento = getDocumentoCacheado(idFirma);
        // eliminamos el documento de la cache
        removeDocumentoCacheado(idFirma);
        removeDocumentoCacheado(sessionID);

        // el siguiente paso es validar el certificado con las CRLs.
        CodigoError codigo = validador.validar(certificadoX509);// CodigoError.
        // OK_CERTIFICADO_VALIDADO
        // ;

        if (codigo == CodigoError.OK_CERTIFICADO_VALIDADO) {
            log.info("El certificado utilizado es correcto. Custodiamos el documento");
            try {
                CertificadoGenerico certificado = CertificadoGenericoFactory.getInstance().generar(certificadoX509);

                // Custodiamos el documento
                Custodia custodia = Custodia.getInstance();
                String newIdfirmaCustodia = custodia.custodiar(idFirma, xmlSignedDocument,
                        documento.getTypeFormatSign());
                log.info("Identificador de custodia asignado al documento firmado: " + newIdfirmaCustodia);

                // invoco al conector Utilizado para que retorne la respuesta al
                // usuario
                // por ahora el conector sera OpenIDServlet
                request.setAttribute("CodigoError", codigo);
                request.setAttribute("certificado", certificado);
                request.setAttribute("newIdfirmaCustodia", newIdfirmaCustodia);

                // TODO colocar un getConectorutilizado y utilizar un metoro
                // redirectMe del conector.
                request.getRequestDispatcher("/conectorFirmaOpenId").forward(request, response);
                // response.sendRedirect(aplicacion.getUrlRetorno());
            } catch (IOException e) {
                // TODO Mejorar el control de excepciones
                e.printStackTrace();
            } catch (ServletException e) {
                // TODO Mejorar el control de excepciones
                e.printStackTrace();
            } catch (ExcepcionErrorInterno e) {
                log.fatal("El sistema de custodia no esta disponible en este momento", e);
                log.info("El certificado no ha sido validado, redireccion a una pagina de error de validacin.");
                request.setAttribute("codError", e.getCodError());
                FaceletsUtil.forward(request, response, Constantes.URI_ERROR_FIRMA);
            }

        } else {
            log.info("El certificado no ha sido validado, redireccion a una pagina de error de validacin."
                    + codigo);
            request.setAttribute("codError", codigo);
            FaceletsUtil.forward(request, response, Constantes.URI_ERROR_AUTENTICACION);
        }
    }

    /**
     * Firma los datos indicados con el certificado
     * 
     * @param certificadoX509
     *            Certificado del usuario
     * @throws ExcepcionErrorInterno
     * @throws ExcepcionCertificadoNoEncontrado
     * @throws ParserConfigurationException
     */
    public String signByServer(Documento documento, String alias, String password)
            throws ExcepcionErrorInterno, ExcepcionCertificadoNoEncontrado, ParserConfigurationException {
        // generamos el XML signature de los datos a firmar
        Document xmlDocument;
        if (documento.getTypeFormatSign().equals(TypeFormatSign.XMLSIG_ENVELOPING)) {
            xmlDocument = XmlSignUtil.getInstance().signDocument(documento, alias, password);
            // Generamos un identificador de firma al documento
            String idFirma = generarIdentificador();
            // Custodiamos el documento
            Custodia custodia = Custodia.getInstance();
            String newIdfirmaCustodia = custodia.custodiar(idFirma, xmlDocument, documento.getTypeFormatSign());
            log.info("Identificador de custodia asignado al documento firmado por el servidor: "
                    + newIdfirmaCustodia);
            // retornamos el identificador de custodia asignado
            return newIdfirmaCustodia;
        } else if (documento.getTypeFormatSign().equals(TypeFormatSign.XADES_EPES_ENVELOPED)) {
            throw new UnsupportedOperationException(
                    "Esta operacin no esta soportada o implementada en la versin Viafirma GPL/Estandar");
        } else {
            log.error("Formato de firma no soportado");
            return null;
        }

    }

    /**
     * Recupera el documento custodiado con el identificador indicado.
     * 
     * @param idDocumento
     *            Identificador del documento custodiado
     * @return Documento custodiado
     * @throws ExcepcionErrorInterno
     *             No se ha podido recuperar el documento custodiado.
     */
    public Documento getDocumentoCustodiado(String idDocumento) throws ExcepcionErrorInterno {
        try {
            // recuperamos el documento custodiado.
            Custodia custodia = Custodia.getInstance();
            Document xmlSig = custodia.recuperar(idDocumento);

            // Recupero el tipo de formato de firma utilizado.
            Documento documento = XmlSignUtil.getInstance().getDocument(xmlSig, idDocumento);
            if (log.isDebugEnabled())
                log.debug("Recuperado el documento " + documento.getNombre());
            return documento;
        } catch (ExcepcionErrorInterno e) {
            throw e;
        } catch (Exception e) {
            log.fatal("No se recuperar el documento custodiado: " + idDocumento, e);
            throw new ExcepcionErrorInterno(CodigoError.ERROR_INTERNO, e);
        }
    }

    /**
     * Comprueba que el documento indicado conincide con el documento custodiado
     * y es este a su vez es vlido
     * 
     * @param originalData
     * @param id
     * @return
     */
    public FirmaInfoViafirma checkOrignalDocumentSigned(byte[] originalData, String id) {
        FirmaInfoViafirma info = new FirmaInfoViafirma();
        // Por defecto o si hay algun problema es invlido.
        info.setValid(false);

        try {
            // recuperamos el xmlSignature custodiado
            Document documento = Custodia.getInstance().recuperar(id);
            // TODO, permitir la recuperacin de todos los firmantes
            XMLSignature xmlSig = XmlSignUtil.getInstance().getXMLSignatureFormDocument(documento).get(0);
            CertificadoGenerico datosCertificado = CertificadoGenericoFactory.getInstance()
                    .generar(xmlSig.getKeyInfo().getX509Certificate());

            // Volcamos los datos del certificado generico sobre los datos de
            // firma
            info.setCaName(datosCertificado.getCa());
            info.setProperties(info.getProperties());
            info.setNumberUserId(datosCertificado.getNumberUserId());
            info.setFirstName(datosCertificado.getNombre());
            info.setLastName(datosCertificado.getApellido1());

            info.setSignId(id);
            // Validamos el hash.
            boolean isValid = ValidadorHandler.getCurrentInstance().checkHash(originalData, id, xmlSig);
            info.setValid(isValid);
        } catch (ExcepcionErrorInterno e) {
            log.warn(e.getMessage());
            info.setMessage(e.getMessage());
        } catch (KeyResolverException e) {
            log.warn(e.getMessage());
            info.setMessage(e.getMessage());
        }

        return info;
    }

    /**
     * Retorna la metainformacin del usuario asociada al documento custodiado
     * con el identificador indicado.
     * 
     * @param CodFirma
     * @return
     * @throws ExcepcionErrorInterno
     */
    public Map<String, Object> getInfo(String codFirma) throws ExcepcionErrorInterno {
        // recuperamos el xmlSignature custodiado
        Object[] arrayDatos = Custodia.getInstance().recuperarMoreInfo(codFirma);
        Document documento = (Document) arrayDatos[1];
        // TODO, permitir la recuperacin de todos los firmantes
        XMLSignature xmlSig = XmlSignUtil.getInstance().getXMLSignatureFormDocument(documento).get(0);
        try {
            CertificadoGenerico certificado = CertificadoGenericoFactory.getInstance()
                    .generar(xmlSig.getKeyInfo().getX509Certificate());
            Map<String, Object> datos = BeanUtil.getCurrentInstance().beanToMap(certificado);
            DocumentVO documentVO = (DocumentVO) arrayDatos[0];
            datos.put(Constantes.FECHA, documentVO.getDateCreation().toString());
            return datos;
        } catch (Exception e) {
            log.warn(e.getMessage());
            throw new ExcepcionErrorInterno(CodigoError.ERROR_CUSTODIA);
        }
    }

    /**
     * Enva un email firmado al destinatario.
     * 
     * @param texto
     *            Texto del email
     * @param textoHtml
     *            Versin HTML del email
     * @param datosAdjunto
     *            Fichero adjunto si existe.
     * @param toUser
     *            Usuario destino
     * @param alias
     * @param password
     * @throws ExcepcionErrorInterno
     *             No se ha podido enviar el email.
     */
    public void sendSignMailByServer(String subject, String mailTo, String texto, String htmlTexto, String alias,
            String password) throws ExcepcionErrorInterno {
        // Preparamos el envio
        MultiPartEmail mail;
        try {
            mail = SendMailUtil.getCurrentInstance().buildMessage(subject, mailTo, texto, htmlTexto, alias,
                    password);
            // mail.setDebug(true);
            String id = mail.send();
            if (log.isDebugEnabled()) {
                log.debug("Nuevo email enviado, con el identificador: " + id);
                mail.getMimeMessage().writeTo(System.out);
            }
        } catch (ExcepcionErrorInterno e) {
            log.warn(e.getMessage());
            throw new ExcepcionErrorInterno(CodigoError.ERROR_SEND_MAIL, e.getMessage(), e);
        } catch (ExcepcionCertificadoNoEncontrado e) {
            log.warn(e.getMessage());
            throw new ExcepcionErrorInterno(CodigoError.ERROR_SEND_MAIL, e.getMessage(), e);
        } catch (EmailException e) {
            log.warn(e.getMessage());
            throw new ExcepcionErrorInterno(CodigoError.ERROR_SEND_MAIL, e.getMessage(), e);
        } catch (IOException e) {
            log.warn(e.getMessage());
            throw new ExcepcionErrorInterno(CodigoError.ERROR_SEND_MAIL, e.getMessage(), e);
        } catch (MessagingException e) {
            log.warn(e.getMessage());
            throw new ExcepcionErrorInterno(CodigoError.ERROR_SEND_MAIL, e.getMessage(), e);
        }
    }

    /**
     * Generamos un pdf ( para descarga) con el contenido original firmado + el
     * cdigo QR.
     * 
     * @param codFirma
     * @param texto
     *            . Si el texto es null se utiliza como texto el nombre del
     *            certificado firmante.
     * @return
     * @throws ExcepcionErrorInterno
     */
    public byte[] buildInfoQRBarCode(String codFirma, String texto, String textoQR, boolean isPdf)
            throws ExcepcionErrorInterno {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            // recuperamos el xmlSignature custodiado
            Document xmlDocument = Custodia.getInstance().recuperar(codFirma);

            // TODO Soporte de elementos multifirmados.
            XMLSignature xmlSig = XmlSignUtil.getInstance().getXMLSignatureFormDocument(xmlDocument).get(0);

            // Recuperamos el documento
            Documento documento = XmlSignUtil.getInstance().getDocument(xmlDocument, codFirma);
            // Recuperamos el mensaje Digest
            String sha1 = XmlSignUtil.getInstance().getDigestFormated(xmlSig, codFirma);

            String url = URL_PUBLIC_VERIFICATION + codFirma;
            // Verificamos y modificamos si es necesario la URL de custodia
            url = QRCodeUtil.getInstance().buildFriendlyURLIfNeeded(url);
            // Recuperamos los datos firmados digitalmente.
            if (texto == null || textoQR == null) {
                CertificadoGenerico certificado = CertificadoGenericoFactory.getInstance()
                        .generar(xmlSig.getKeyInfo().getX509Certificate());
                String[] etiquetas = QRCodeUtil.getInstance().buildTextoEtiqueta(certificado, codFirma, url, sha1);
                texto = etiquetas[0];
                textoQR = etiquetas[1];
            }

            if (log.isDebugEnabled())
                log.debug("Generando justificante de firma para fichero " + documento.getNombre());
            if (isPdf) {
                if (documento.getTipo().equals(TypeFile.PDF)) {
                    // Generamos un pdf con la firma al pie.
                    QRCodeUtil.getInstance().firmarPDF(new ByteArrayInputStream(documento.getDatos()), texto, url,
                            textoQR, codFirma, out);
                } else if (documento.getTipo().isImagen()) {
                    // Generamos un PDF con la imagen y el pie de firma.
                    QRCodeUtil.getInstance().generarImagenPdf(documento.getDatos(), texto, url, textoQR, codFirma,
                            out);
                } else {
                    // TODO Para el caso particular de la factuaee, crear un
                    // formato de previsualizacin decente.
                    // Generamos un justificante sin contenido.
                    QRCodeUtil.getInstance().generarPdf(texto, url, textoQR, codFirma, out);
                }
            } else {
                return QRCodeUtil.getInstance().generate(texto, url, textoQR, codFirma);
            }

        } catch (ExcepcionErrorInterno e) {
            throw e;
        } catch (Exception e) {
            log.fatal("No se puede adjuntar el Cdigo QR a la firma con cdigo de firma: " + codFirma, e);
            throw new ExcepcionErrorInterno(CodigoError.ERROR_PDF);
        }
        return out.toByteArray();
    }

    // **********************************************
    // Oyente de contexto que activa el nucleo cuando
    // arranca la aplicacion
    // **********************************************

    /**
     * Muestra un mensaje cuando se apacha el Nucleo.
     * 
     * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent)
     */
    @Override
    public void contextDestroyed(ServletContextEvent contextEvent) {
        // apagamos las diferentes caches.
        cacheCertificados.getCacheManager().removalAll();
        CacheManager.getInstance().shutdown();
        // Detenemos el registro RMI
        if (rmiRegistry != null) {
            try {
                rmiRegistry.unbind(Constantes.NOMBRE_CONECOR_RMI_PUBLICADO);
                // Detenemos el registro RMI.
                UnicastRemoteObject.unexportObject(rmiRegistry, true);
                rmiRegistry = null;
            } catch (Exception e) {
                log.warn("No se puede detener el registro RMI del motor de formularios." + e.getMessage());
            }
            log.info("Desactivando el registro RMI");
        }
        // Desactivamos el manejador de conexiones http
        MultiThreadedHttpConnectionManager.shutdownAll();

        System.out.println("Nucleo Viafirma apagado. " + this);
        super.contextDestroyed(contextEvent);

    }

    /**
     * Oyente de Contexto que se activa al arrancar la aplicacin.
     * 
     * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)
     */
    @Override
    public void contextInitialized(ServletContextEvent event) {
        System.out.println("*******************************************\n\t\tIniciando Viafirma\n" + this
                + "\n*******************************************");
        init(event.getServletContext());
        loadProperties(event.getServletContext());
    }

    // *********************************************
    // Mtodos de utilidad
    // *********************************************

    /**
     * Imprime el Copyright del Ncleo
     * 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "Plataforma de Autenticacin y Firma Digital Viafirma.\n Copyright 2006-2008 Viavansi (www.viavansi.com)";
    }

    /**
     * Almacena un certificado en cache a la espera de que este sea procesado en
     * otro momento.
     * 
     * @param key
     *            Identificador del certificado
     * @param certificadoX509
     *            Certificado almacenado
     */
    public void cachearCertificado(String key, X509Certificate certificadoX509) {
        cacheCertificados.put(new Element(key, certificadoX509));
    }

    /**
     * Recupera el certificado cacheado
     * 
     * @param key
     *            Identificador del certificado
     */
    public X509Certificate getCertificadoCacheado(String key) {
        Element elemento = cacheCertificados.get(key);
        if (elemento == null) {
            return null;
        } else {
            // elimino el elemento de la cache ya que ya no nos es necesario
            cacheCertificados.remove(key);
            return (X509Certificate) elemento.getValue();
        }
    }

    /**
     * Recupera el documento cacheado
     * 
     * @param key
     *            Identificador del documento
     */
    public Documento getDocumentoCacheado(String key) {
        Element elemento = cacheDocumentToSign.get(key);
        return (Documento) elemento.getValue();
    }

    /**
     * Elimina de la cache el fichero con el identificador indicado.
     * 
     * @param key
     */
    public void removeDocumentoCacheado(String key) {
        try {
            cacheDocumentToSign.remove(key);
        } catch (Exception e) {
            log.warn("El fichero " + key + " ya ha sido eliminado.");
        }
    }

    /**
     * Cachea un xml signature.
     * 
     * @param key
     * @param signature
     */
    public void cachearXmlDocument(String key, Document signature) {
        cacheDocumentToSign.put(new Element(key, signature));
    }

    /**
     * Recupera de la cache el xml signature
     * 
     * @param key
     * @param signature
     * @return
     */
    public Document getXMLDocument(String key) {
        Element elemento = cacheDocumentToSign.get(key);
        return (Document) elemento.getObjectValue();
    }

    /**
     * @param warning_certificado_no_enviado
     * @param request
     * @param response
     */
    public void redireccionarError(CodigoError warning_certificado_no_enviado, HttpServletRequest request,
            HttpServletResponse response) {
        // Metemos en request los motivos del error
        request.setAttribute("codError", warning_certificado_no_enviado);
        FaceletsUtil.forward(request, response, Constantes.URI_ERROR_AUTENTICACION);
    }

    /**
     * Genera un identificador nico. Temporal. posteriormente Custodia generara
     * uno definitivo.
     * 
     * @return
     */
    private String generarIdentificador() {
        return CadenaUtilities.getCurrentInstance().generarRandomIdentificadorTimeStamp();
    }

    /**
     * Implementacin del patron Singleton.
     * Nota: El singleton se configura en el arranque de la aplicacin.
     */
    private static Nucleo singleton;

    public Nucleo() {
        super();
    }

    /*
     * (non-Javadoc)
     * 
     * @seecom.viavansi.framework.core.servlet.StartupServletContextListener#
     * getClaseDeConstantes()
     */
    @Override
    protected Class<?> getClaseDeConstantes() {
        // No se esta utilizando una clase de constantes Global dela aplicacin.
        return null;
    }

}