Java tutorial
/* 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.validacion; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.security.cert.CertPathValidatorException; import java.security.cert.TrustAnchor; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.Properties; import java.util.Set; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.xml.security.signature.XMLSignature; import org.apache.xml.security.utils.Constants; import org.apache.xml.security.utils.XMLUtils; import org.apache.xpath.XPathAPI; import org.bouncycastle.util.encoders.Base64; import org.viafirma.cliente.firma.TypeFormatSign; import org.viafirma.excepciones.CodigoError; import org.viafirma.excepciones.ExcepcionErrorInterno; import org.viafirma.util.Constantes; import org.viafirma.util.XmlSignUtil; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * Maneja todos los procesos relacionados con la validacin de certificados. * * @author Felix Garcia Borrego (borrego at gmail.com) */ public class ValidadorHandler { /** * Manejador para las validaciones de certificados basadas en CRLs. */ private CRLValidationHandler crlValidationHandler; /** * Manejador para las validaciones de certificados basadas en OCSP. */ private OcspValidatorHandler ocspValidationHandler; private static ValidadorHandler singleton; private Set<TrustAnchor> certificadosConfianza; public static ValidadorHandler getCurrentInstance() { if (singleton == null) { singleton = new ValidadorHandler(); } return singleton; } private ValidadorHandler() { // Singleton } /** * Crea un validador de certificados. Normalmente solo hay una instancia de * esta clase y esta contenida en el Nucleo. Solo el nucleo debe invocar * este constructor * * @param validacionOnline * @param properties * Propiedades para de conexin a crls */ public static void init(boolean validacionOnline, Properties properties) { ValidadorHandler validador = getCurrentInstance(); // Recuperamos los certificados de confianza que la plataforma puede // validar. validador.certificadosConfianza = validador.recuperarCertificadosDeConfianza(); validador.crlValidationHandler = new CRLValidationHandler(validacionOnline, validador.certificadosConfianza); validador.ocspValidationHandler = new OcspValidatorHandler(validacionOnline, validador.certificadosConfianza); // Inicializa el acceso a las crls CRLUtil.init(properties); } /** * Valida el certificado indicado. Utilizando segn el tipo validacin OCSP * o CRL. * * @param certificadoX509 * @return */ public CodigoError validar(X509Certificate certificadoX509) { // Si el protocolo es OCSP.... if (isOCSPProtocol(certificadoX509)) { if (log.isDebugEnabled()) log.debug("Validando con OCSP el certificado : " + certificadoX509.getSubjectDN().getName()); try { return ocspValidationHandler.validarOCSP(certificadoX509); } catch (CertPathValidatorException e) { log.warn(e.getMessage()); return CodigoError.ERROR_OCSP_INTERNAL_ERROR; } } else { // el certificado sera validado utilizando el mtodo de acceso a // CRLs if (log.isDebugEnabled()) log.debug("Validando certificado : " + certificadoX509.getSubjectDN().getName()); return crlValidationHandler.validarCRL(certificadoX509); } } /** * Inizializa el conjunto de certificados altualmente aceptados como de * confianza y con los que la plataforma funciona correctamente. * */ private Set<TrustAnchor> recuperarCertificadosDeConfianza() { // recupero el conjunto de certificados de confianza que hay en el // sistema Set<TrustAnchor> certificadosConfianza = KeyStoreLoader.getJSSETrustAnchors(); if (certificadosConfianza.size() == 0) { log.error( "No hay certificados de confianza, es necesario que almenos exista un certificado de confianza."); } return certificadosConfianza; } /** * Chequea el hash del documento original coincide con el hash del documento * custodiado. * * @param originalData * @param id * @param xmlSig * @return */ public boolean checkHash(byte[] originalData, String id, XMLSignature xmlSig) { // Realiza el digest del documento custodiado String digestCustodiadoString = XmlSignUtil.getInstance().getDigest(xmlSig, id)[1]; // Lo pasamos a Byte[] byte[] digestCustodiado = Base64.decode(digestCustodiadoString); // Bytes del documento original preprocesados ( Canonizados si fuese // necesario) byte[] originalDataPreprocess = null; try { // Recuperamos el tipo de documento a comprobar TypeFormatSign typeFormatSign = XmlSignUtil.getTypeFormatSign(id); if (typeFormatSign == TypeFormatSign.XMLSIG_ENVELOPING) { // Para este tipo de formato no es necesario canonizar, ej: // binario de un PDF originalDataPreprocess = originalData; } else if (typeFormatSign == TypeFormatSign.XADES_EPES_ENVELOPED) { // Recupera el documento custodiado InputStream input = new ByteArrayInputStream(originalData); // Reader readerXML = new InputStreamReader(input); InputSource ioXml = new InputSource(input); // parser javax.xml.parsers.DocumentBuilderFactory dbf = javax.xml.parsers.DocumentBuilderFactory .newInstance(); javax.xml.parsers.DocumentBuilder db; dbf.setNamespaceAware(true); dbf.setAttribute("http://xml.org/sax/features/namespaces", Boolean.TRUE); db = dbf.newDocumentBuilder(); org.w3c.dom.Document doc = db.parse(ioXml); org.w3c.dom.Element nscontext = XMLUtils.createDSctx(doc, "ds", Constants.SignatureSpecNS); NodeList nodeIterator = XPathAPI.selectNodeList(doc, "//ds:Signature", nscontext); // Extrae los hijos que ds:signature for (int i = 0; i < nodeIterator.getLength(); i++) { Node node = nodeIterator.item(i); node.getParentNode().removeChild(node); } // Canoniza el documento originalDataPreprocess = XmlSignUtil.getInstance().canonizar(doc); } else { throw new UnsupportedOperationException( "El tipo de firma no es soportado para su verificacion. " + typeFormatSign); } // Digiere los datos a comprobar byte[] digestOriginal = DigestUtils.sha(originalDataPreprocess); // Comprueba que los disges son efectivamente iguales if (Arrays.equals(digestCustodiado, digestOriginal)) { return true; } return false; } catch (ParserConfigurationException e) { log.error("Error con el configurador al parsear el documento ", e); } catch (SAXException e) { log.error("Error de nivel Sax al parsear el documento ", e); } catch (IOException e) { log.error("Error de flujo al parsear el documento", e); } catch (TransformerException e) { log.error("Error al transformar el documento", e); } catch (ExcepcionErrorInterno e) { log.error("Error de seguridad al digerir el documento", e); } return false; } private Log log = LogFactory.getLog(ValidadorHandler.class); /** * * Comprueba si el protocolo utilizado es OCSP * * @param certificadoX509 * @return */ public boolean isOCSPProtocol(X509Certificate certificadoX509) { boolean isEDNI = certificadoX509.getIssuerDN().getName().contains(Constantes.EDNI_ISSUERDN); // Si es de tipo FNMT y..... boolean isFNMT = certificadoX509.getIssuerDN().getName().contains(Constantes.FNMT_ISSUERDN); // Comprobamos si no hay parametros de configuracin para FNMT LDAP. boolean hayFNMTViaOCSP = StringUtils.isEmpty(CRLUtil.getCurrentInstance().fnmtLDAPHostURL); if (isEDNI || (isFNMT && hayFNMTViaOCSP)) { return true; } return false; } /** Retorna el conjunto de certificados de confianza. * @return Returns the certificadosConfianza. */ public Set<TrustAnchor> getCertificadosConfianza() { return certificadosConfianza; } }