/**
* LICENCIA LGPL:
*
* Esta librería es Software Libre; Usted puede redistribuirlo y/o modificarlo
* bajo los términos de la GNU Lesser General Public License (LGPL)
* tal y como ha sido publicada por la Free Software Foundation; o
* bien la versión 2.1 de la Licencia, o (a su elección) cualquier versión posterior.
*
* Esta librería se distribuye con la esperanza de que sea útil, pero SIN NINGUNA
* GARANTÍA; tampoco las implícitas garantías de MERCANTILIDAD o ADECUACIÓN A UN
* PROPÓSITO PARTICULAR. Consulte la GNU Lesser General Public License (LGPL) para más
* detalles
*
* Usted debe recibir una copia de la GNU Lesser General Public License (LGPL)
* junto con esta librería; si no es así, escriba a la Free Software Foundation Inc.
* 51 Franklin Street, 5º Piso, Boston, MA 02110-1301, USA o consulte
* <http://www.gnu.org/licenses/>.
*
* Copyright 2008 Ministerio de Industria, Turismo y Comercio
*
*/
package es.mityc.firmaJava.libreria.xades;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.net.Authenticator;
import java.net.URLDecoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import java.security.cert.CRLException;
import java.security.cert.CertStoreException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
import javax.security.auth.x500.X500Principal;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xml.security.Init;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.keys.KeyInfo;
import org.apache.xml.security.keys.keyresolver.KeyResolverException;
import org.apache.xml.security.signature.XMLSignature;
import org.apache.xml.security.signature.XMLSignatureException;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.DERInteger;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.ocsp.ResponderID;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.ocsp.BasicOCSPResp;
import org.bouncycastle.ocsp.CertificateID;
import org.bouncycastle.ocsp.OCSPException;
import org.bouncycastle.ocsp.OCSPResp;
import org.bouncycastle.ocsp.RevokedStatus;
import org.bouncycastle.ocsp.SingleResp;
import org.bouncycastle.tsp.TSPException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import es.mityc.firmaJava.configuracion.Configuracion;
import es.mityc.firmaJava.configuracion.EnumFormatoFirma;
import es.mityc.firmaJava.libreria.ConstantesXADES;
import es.mityc.firmaJava.libreria.utilidades.Base64;
import es.mityc.firmaJava.libreria.utilidades.Base64Coder;
import es.mityc.firmaJava.libreria.utilidades.I18n;
import es.mityc.firmaJava.libreria.utilidades.NombreNodo;
import es.mityc.firmaJava.libreria.utilidades.SimpleAuthenticator;
import es.mityc.firmaJava.libreria.utilidades.UtilidadCertificados;
import es.mityc.firmaJava.libreria.utilidades.UtilidadFechas;
import es.mityc.firmaJava.libreria.utilidades.UtilidadFirmaElectronica;
import es.mityc.firmaJava.libreria.utilidades.UtilidadTratarNodo;
import es.mityc.firmaJava.libreria.utilidades.Utilidades;
import es.mityc.firmaJava.libreria.xades.elementos.SignaturePolicyIdentifier;
import es.mityc.firmaJava.libreria.xades.errores.BadFormedSignatureException;
import es.mityc.firmaJava.libreria.xades.errores.FirmaXMLError;
import es.mityc.firmaJava.libreria.xades.errores.InvalidInfoNodeException;
import es.mityc.firmaJava.libreria.xades.errores.PolicyException;
import es.mityc.firmaJava.ocsp.RespuestaOCSP;
import es.mityc.firmaJava.ocsp.RespuestaOCSP.TIPOS_RESPONDER;
import es.mityc.firmaJava.policy.IValidacionPolicy;
import es.mityc.firmaJava.policy.PoliciesManager;
import es.mityc.firmaJava.policy.PolicyResult;
import es.mityc.firmaJava.ts.TSCliente;
import es.mityc.firmaJava.ts.TSClienteError;
import es.mityc.firmaJava.ts.TSPAlgoritmos;
import es.mityc.firmaJava.ts.TSValidacion;
/**
* Clase para la validación de la firmas XADES
*
* @author Ministerio de Industria, Turismo y Comercio
* @version 0.9 beta
*/
public class ValidarFirmaXML implements ConstantesXADES
{
private Configuracion configuracion = null;
private Document doc = null;
private Element firmaAValidar = null;
private List<String> esquemasParaValidar = new LinkedList<String>();
private String esquema = null;
private boolean esValido = false;
private ResultadoValidacion resultado = new ResultadoValidacion();
private DatosFirma datosFirma = null;
private ArrayList<DatosSelloTiempo> arrayDatosSello = new ArrayList<DatosSelloTiempo>();
private ArrayList<DatosCRL> arrayDatosCRL = new ArrayList<DatosCRL>();
private ArrayList<DatosOCSP> arrayDatosOCSP = new ArrayList<DatosOCSP> ();
private ArrayList<PolicyResult> politicas = new ArrayList<PolicyResult> ();
private ArrayList<X509Certificate> cadenaCertificados = new ArrayList<X509Certificate> ();
private DatosTipoFirma tipoDocFirma = null;
private static Log log = LogFactory.getLog(ValidarFirmaXML.class);
protected class EstructuraFirma {
String esquema;
Element firma;
Element signedSignatureProperties;
Element unsignedSignatureProperties;
}
/**
* Crea una nueva instancia de ValidarFirmaXML
*/
public ValidarFirmaXML()
{
configuracion = new Configuracion();
configuracion.cargarConfiguracion() ;
}
/**
* Crea una nueva instancia de ValidarFirmaXML con una configuración ya cargada
*/
public ValidarFirmaXML(Configuracion configuracion)
{
this.configuracion = configuracion;
}
/**
* Valida la firma XML
* @param firmaParaValidar firchero con la firma XADES para validar
* @param policies lista de validadores de policies que se aplicarán en la validación (<code>null</code> si no hay policies a aplicar).
* @return ValidationResult Este objeto indica si la firma es válida o no, y en este último caso
* indica la razón por la cual la firma no es válida
* @throws FirmaXMLError Si la firma no es válida
*/
public ResultadoValidacion validar(File firmaParaValidar, ArrayList<IValidacionPolicy> policies) throws FirmaXMLError
{
FileInputStream fis = null;
try {
fis = new FileInputStream(firmaParaValidar);
} catch (FileNotFoundException e) {
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR58));
return null;
}
FirmaXMLError excepcion = null;
ResultadoValidacion rs = null;
try {
rs = validar(fis, firmaParaValidar.getParent(), policies);
} catch (FirmaXMLError ex) {
excepcion = ex;
} finally {
try {
// AppPerfect: Falso positivo
fis.close();
} catch (Exception ex) {}
}
if (excepcion != null)
throw excepcion;
return rs;
}
/**
* @param bFirmaParaValidar
* @return
* @throws FirmaXMLError
*/
public ResultadoValidacion validar(byte[] bFirmaParaValidar, ArrayList<IValidacionPolicy> policies) throws FirmaXMLError
{
// No se ha proporcionado la ruta sobre la que esta el documento a validar. Se toma por defecto XXXX
log.debug(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_INFO2) + ESPACIO
+ System.getProperty(USER_DIR));
return validar(bFirmaParaValidar, System.getProperty(USER_DIR), policies);
}
public ResultadoValidacion validar(byte[] bFirmaParaValidar, String path, ArrayList<IValidacionPolicy> policies) throws FirmaXMLError
{
ByteArrayInputStream bis = null;
bis = new ByteArrayInputStream(bFirmaParaValidar);
return validar(bis, path, policies);
}
/**
* Valida la firma XML
* @param firmaParaValidar firchero con la firma XADES para validar
* @param path ruta donde se encuentran los ficheros de complemento de información (para XADES-C)
* @param policies lista de validadores de policies que se han de aplicar en la validación
* @return ValidationResult Este objeto indica si la firma es válida o no, y en este último caso
* indica la razón por la cual la firma no es válida
* @throws FirmaXMLError Si la firma no es válida
*/
public ResultadoValidacion validar(InputStream inputFirmaParaValidar, String path, ArrayList<IValidacionPolicy> policies) throws FirmaXMLError
{
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true) ;
DocumentBuilder db = null;
try {
db = dbf.newDocumentBuilder();
} catch (ParserConfigurationException e1) {
mostrarErrorValidacion(e1);
}
InputSource isour = null;
try {
isour = new InputSource(inputFirmaParaValidar);
doc = db.parse(isour);
} catch (FileNotFoundException e1) {
mostrarErrorValidacion(e1);
} catch (SAXException e1) {
mostrarErrorValidacion(e1);
} catch (IOException e1) {
mostrarErrorValidacion(e1);
}
return validar(doc, path, policies);
}
public ResultadoValidacion validar(Document doc, String path, ArrayList<IValidacionPolicy> policies) throws FirmaXMLError
{
ArrayList<ResultadoValidacion> resultados = new ArrayList<ResultadoValidacion>();
Security.addProvider(new BouncyCastleProvider());
//Establece el idioma según la configuración
String locale = configuracion.getValor(LOCALE);
// Configura el idioma
I18n.setLocale(locale, locale.toUpperCase());
// Se recupera la lista de esquemas que se pueden validar
// Se recomienda mantener el orden de esquemas de superior a inferior, ya que los primeros son más completos
final String cadenaDeUrisXadesNS = configuracion.getValor(LIBRERIAXADES_VALIDARXADES);
StringTokenizer uriXadesNS = new StringTokenizer(cadenaDeUrisXadesNS, COMA);
boolean valor = uriXadesNS.hasMoreTokens();
while(valor)
{
esquema = uriXadesNS.nextToken();
esquemasParaValidar.add(esquema);
valor = uriXadesNS.hasMoreTokens();
}
if (esquemasParaValidar.isEmpty())
{
// No se han encontrado esquemas para validar en el fichero de propiedades
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR1));
throw new FirmaXMLError(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR1));
}
// Validamos que el fichero de firma sea un XML bien formado
Init.init() ;
// Se recupera el nodo de firma Signature
NodeList listaFirmas = doc.getElementsByTagNameNS(SCHEMA_DSIG, LIBRERIAXADES_SIGNATURE);
if (listaFirmas.getLength() == 0)
{
// Error en la validación. No se pudo encontrar el nodo de firma
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR2));
throw new FirmaXMLError(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR2));
}
// Si el documento tiene múltiples firmas existe más de un nodo de firma
log.debug(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_INFO1) + ESPACIO + listaFirmas.getLength()); // Nº Firmas presentes
// Cada firma se valida como una firma única
int longitud = listaFirmas.getLength();
for(int i=0; i<longitud; i++)
{
resultados.add(validaFirma(listaFirmas.item(i), path, policies));
}
configuracion = null;
firmaAValidar = null;
doc = null;
return resultados.get(0);
}
private ResultadoValidacion validaFirma(Node firma, String path, ArrayList<IValidacionPolicy> policies) throws FirmaXMLError {
// pasa las políticas indicadas al array de resultados de las políticas
if ((policies != null) && (policies.size() > 0)) {
Iterator<IValidacionPolicy> it = policies.iterator();
while (it.hasNext()) {
IValidacionPolicy valPol = it.next();
if (valPol != null) {
PolicyResult pr = new PolicyResult();
pr.setPolicyVal(valPol);
politicas.add(pr);
}
}
}
resultado = new ResultadoValidacion();
X509Certificate cert = null;
XMLSignature firmaDocumento = null;
String uriDS = null;
try {
firmaDocumento = new XMLSignature((Element)firma,CADENA_VACIA);
uriDS = firmaDocumento.getBaseNamespace();
} catch (XMLSignatureException e) {
// Error en la validación. Se produjo un error
log.error(e.getMessage(), e);
throw new FirmaXMLError(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR7) +
DOS_PUNTOS_ESPACIO + e.getMessage());
} catch (XMLSecurityException e) {
log.error(e.getMessage(), e);
throw new FirmaXMLError(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR7) +
DOS_PUNTOS_ESPACIO + e.getMessage());
}
// Si existe el nodo KeyInfo, obtenemos el certificado firmante
KeyInfo ki = firmaDocumento.getKeyInfo();
if (ki != null) {
try {
cert = ki.getX509Certificate();
} catch (KeyResolverException ex) {
// Error en la validación. No se pudo obtener el certificado firmante
log.error(ex.getMessage());
throw new FirmaXMLError(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR3));
}
if (cert != null)
{
try {
esValido = firmaDocumento.checkSignatureValue(cert);
} catch (XMLSignatureException ex) {
log.info(ex.getMessage(), ex);
// Error en la validación. La firma no tiene un formato correcto
throw new FirmaXMLError(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR4));
}
}
else {
// Error en la validación. No se ha encontrado el certificado
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR5));
throw new FirmaXMLError(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR5));
}
}
else {
// Error en la validación. KeyInfo del certificado no encontrado
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR6));
throw new FirmaXMLError(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR6));
}
// Se instancia la estructura que almacena los datos de la firma
datosFirma = new DatosFirma();
resultado.setDatosFirma(datosFirma);
// Se almacena el certificado de firma en la cadena de certificados hasta que toda la cadena sea generada (ver validarXadesBes y C)
cadenaCertificados.add(cert);
datosFirma.setCadenaFirma(UtilidadCertificados.convertCertPath(cadenaCertificados));
// Se obtiene el nodo raíz de la firma si la firma es válida
firmaAValidar = (Element)firma;
// Se obtiene el esquema
EstructuraFirma estructuraFirma = obtenerEsquema(firmaAValidar); // En este punto se obtiene el esquema de la firma
if (estructuraFirma == null) {
// No se han encontrado esquemas para validar en el fichero de propiedades
log.error(I18n.getResource(ConstantesXADES.LIBRERIAXADES_VALIDARFIRMA_ERROR1));
throw new FirmaXMLError(I18n.getResource(ConstantesXADES.LIBRERIAXADES_VALIDARFIRMA_ERROR1));
} else {
esquema = estructuraFirma.esquema;
datosFirma.setEsquema(XAdESSchemas.getXAdESSchema(esquema));
}
// Una vez que sabemos que la firma es una firma XADES bien formada verificamos de qué tipo
// específico de firma se trata: XADES-BES, XAdES-EPES, XADES-T, XAdES-C, XAdES-X, XADES-XL o XAdES-A
try {
tipoDocFirma = tipoFirma(firmaAValidar, esquema);
datosFirma.setTipoFirma(tipoDocFirma);
} catch (BadFormedSignatureException e) {
log.error(e.getMessage());
resultado.setLog(I18n.getResource(ConstantesXADES.LIBRERIAXADES_VALIDARFIRMA_ERROR4));
resultado.setValidate(false);
resultado.setResultado(ResultadoEnum.INVALID);
esValido = false;
tipoDocFirma = new DatosTipoFirma(EnumFormatoFirma.XMLSignature, false, false);
}
if (esValido && datosFirma.getTipoFirma().esXAdES_A()) {
// No se soporta el formato de firma XAdES A
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR156));
esValido = false;
resultado.setValidate(false);
resultado.setResultado(ResultadoEnum.UNKNOWN);
}
// Si es una firma XADES-BES válida continúa con el proceso de validación
if (esValido)
{
if (validarXadesBes(path, estructuraFirma))
{
if (datosFirma.getTipoFirma().esXAdES_EPES()) { // El documento es una firma XADES-EPES válida. Validación incompleta
log.info(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_TEXTO5));
resultado.setNivelValido(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_TEXTO5));
} else { // El documento es una firma XADES-BES válida. Validación incompleta
log.info(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_TEXTO1));
resultado.setNivelValido(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_TEXTO1));
}
resultado.setEnumNivel(EnumFormatoFirma.XAdES_BES);
// La validación no se puede completar con la información contenida en el fichero de firma
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_TEXTO6));
// Almacenamos la fecha de Firma
datosFirma.setFechaFirma(obtenerFechaFirma());
// Almacenamos los roles de la firma
datosFirma.setRoles(obtenerRoles(estructuraFirma));
// Validación para XADES-T. Realiza la validación tanto si la firma es XADES-T como XADES-XL
if((tipoDocFirma.getTipoXAdES()).compareTo(EnumFormatoFirma.XAdES_BES) > 0)
{
// Valida el sello de tiempo de la firma XADES-T
if(validarSelloTiempoXadesT())
{
// El documento es una firma XADES-T válida. Validación incompleta
log.info(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_TEXTO2));
resultado.setNivelValido(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_TEXTO2));
resultado.setEnumNivel(EnumFormatoFirma.XAdES_T);
datosFirma.setDatosSelloTiempo(arrayDatosSello);
// Valida los campos CompleteCertificateRefs y CompleteRevocationRefs de la firma XADES-C
if ((tipoDocFirma.getTipoXAdES()).compareTo(EnumFormatoFirma.XAdES_T) > 0) {
// Se valida XADES-C
// AppPerfect: Falso positivo
if (validarXadesC(path, uriDS, cert)) {
log.info(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_TEXTO4));
resultado.setNivelValido(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_TEXTO4));
resultado.setEnumNivel(EnumFormatoFirma.XAdES_C);
resultado.setLog(CADENA_VACIA); // Se borra el mensaje de validación incompleta
datosFirma.setDatosOCSP(arrayDatosOCSP);
datosFirma.setDatosCRL(arrayDatosCRL);
// Validación para XADES-XL. Solo se realiza si la firma es XADES-XL
if((tipoDocFirma.getTipoXAdES()).compareTo(EnumFormatoFirma.XAdES_X) >= 0)
{
// La firma XADES-XL está formado por otros formatos de firma XADES intermedios:
// XADES-X y XADES-C que es necesario validar antes de validar XADES-XL
// Validamos el sello de tiempo de XADES-X (La firma XAdES_XL quedó validada al validar XAdES-C)
// AppPerfect: Falso positivo
if (validarSelloTiempoXadesXTipo1() || validarSelloTiempoXadesXTipo2())
{
// La firma es una XAdES-X/XL válida
resultado.setEnumNivel(tipoDocFirma.getTipoXAdES());
if (EnumFormatoFirma.XAdES_X.compareTo(tipoDocFirma.getTipoXAdES()) == 0) {
// El documento es una firma XAdES-X válida
resultado.setNivelValido(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_TEXTO7));
log.info(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_TEXTO7));
} else {
// El documento es una firma XAdES-XL válida
resultado.setNivelValido(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_TEXTO3));
log.info(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_TEXTO3));
}
datosFirma.setDatosSelloTiempo(arrayDatosSello);
datosFirma.setDatosOCSP(arrayDatosOCSP);
} else {
// No se encuentra el segundo sello de tiempo correspondiente al nivel XAdES-X
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR103));
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR104));
esValido = false;
}
}
}
}
}
else
{
// Si el sello de tiempo de la firma XADES-T no es válido, se modifica la respuesta ya se que
// que la firma XADES-T está incluida dentro de una firma XADES-XL o no. En el primer caso se
// debe distinguir el primer sello de tiempo perteneciente a XADES-T del segundo sello de
// tiempo perteneciente a XADES-X
String sello = CADENA_VACIA;
if ((tipoDocFirma.getTipoXAdES()).compareTo(EnumFormatoFirma.XAdES_XL)==0)
sello = LIBRERIAXADES_PRIMER;
// El valor del sello de tiempo de la firma no es válido
log.info(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR8) + ESPACIO +
sello + ESPACIO +
I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR9) + ESPACIO +
(tipoDocFirma.getTipoXAdES()) + ESPACIO +
I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR10));
// Se escribe el log de sello de tiempo inválido si el Log contiene el mensaje por defecto (para no sobreescribirlo)
if (I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_TEXTO6).equals(resultado.getLog())) {
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR8) + ESPACIO +
sello + ESPACIO +
I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR9) + ESPACIO +
(tipoDocFirma.getTipoXAdES()) + ESPACIO +
I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR10));
}
esValido = false;
}
}
}
resultado.setValidate(esValido);
resultado.setResultado(esValido?ResultadoEnum.VALID:ResultadoEnum.INVALID);
// Validación del Nodo Policy del nivel EPES, si existe
if (esValido) {
buscaXadesEpes();
}
}
resultado.setDoc(doc);
// Valida las policies indicadas
if (esValido && (politicas.size() > 0)) {
Iterator<PolicyResult> it = politicas.iterator();
while (it.hasNext()) {
PolicyResult pr = it.next();
if (pr.getPolicyVal() == null) {
// Política desconocida
pr.setPolicyResult(I18n.getResource(ConstantesXADES.LIBRERIAXADES_VALIDARFIRMA_ERROR105));
} else {
try {
IValidacionPolicy valPol = pr.getPolicyVal();
pr.setPolicyId(valPol.getIdentidadPolicy());
valPol.validaPolicy((Element)firma, resultado);
} catch (PolicyException ex) {
log.error(ex.getMessage(), ex);
resultado.setValidate(false);
resultado.setResultado(ResultadoEnum.INVALID);
resultado.setLog(ex.getMessage());
pr.setPolicyResult(ex.getMessage());
break;
} catch (Throwable th) {
// Error validando la política: XXXX
log.error(I18n.getResource(ConstantesXADES.LIBRERIAXADES_VALIDARFIRMA_ERROR106) +
DOS_PUNTOS_ESPACIO + th.getMessage(), th);
resultado.setValidate(false);
resultado.setResultado(ResultadoEnum.INVALID);
// Error al validar una policy. Validez de la firma desconocida
resultado.setLog(I18n.getResource(ConstantesXADES.LIBRERIAXADES_VALIDARFIRMA_ERROR71));
pr.setPolicyResult(th.getMessage());
break;
}
}
}
}
if (politicas.size() > 0)
datosFirma.setPoliticas(politicas);
return resultado;
}
/**
* Busca si la firma tiene indicada alguna política y la añade al listado de políticas a validar.
*
* @param policies listas de políticas a validar
* @return
*/
private void buscaXadesEpes() {
// Se valida la politica de firmas si existe el nodo
NodeList signaturePolicyList = firmaAValidar.getElementsByTagNameNS(esquema, ConstantesXADES.LIBRERIAXADES_POLICY_SIGNATUREPOLICYIDENTIFIER);
if (signaturePolicyList.getLength() == 1) {
// Se recoge el nodo SignaturePolicyIdentifier
Element signaturePolicyIdentifierNode = (Element) signaturePolicyList.item(0);
SignaturePolicyIdentifier spi;
try {
spi = new SignaturePolicyIdentifier(XAdESSchemas.getXAdESSchema(esquema));
if (!spi.isThisNode(signaturePolicyIdentifierNode))
throw new InvalidInfoNodeException(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR107));
spi.load((signaturePolicyIdentifierNode));
} catch (InvalidInfoNodeException ex) {
// El nodo encontrado no es un SignaturePolicyIdentifier válido
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR108));
// Información de política mal formada
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR109));
esValido = false;
resultado.setValidate(false);
resultado.setResultado(ResultadoEnum.INVALID);
return;
}
String clave = ConstantesXADES.LIBRERIAXADES_IMPLIEDPOLICY_MANAGER;
if (!spi.isImplied())
clave = Utilidades.binary2String(Base64Coder.decode(spi.getSignaturePolicyId().getSigPolicyHash().getValue().getValue()));
// Se busca el validador asociado
PoliciesManager policiesManager = PoliciesManager.getInstance();
IValidacionPolicy valPol = policiesManager.getValidadorPolicy(clave);
if (valPol == null) {
PolicyResult pr = new PolicyResult();
if (spi.isImplied())
pr.setPolicyId(ConstantesXADES.LIBRERIAXADES_IMPLIEDPOLICY_MANAGER);
else {
String polId = spi.getSignaturePolicyId().getSigPolicyId().getIdentifier().getUri().toString();
try {
polId = URLDecoder.decode(polId, UTF8);
} catch (UnsupportedEncodingException ex) {}
// La firma contiene políticas desconocidas
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR102));
pr.setPolicyId(polId);
}
politicas.add(pr);
} else if (!politicas.contains(valPol)) {
PolicyResult pr = new PolicyResult();
pr.setPolicyVal(valPol);
politicas.add(pr);
}
}
else if (signaturePolicyList.getLength() > 1) {
// Demasiadas políticas en un nodo de firma
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR110));
// Modelo de políticas no soportado
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR111));
esValido = false;
resultado.setValidate(false);
resultado.setResultado(ResultadoEnum.INVALID);
return;
}
}
private void mostrarErrorValidacion(Exception ex) throws FirmaXMLError{
// El documento de firma no es un documento XML bien formado
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR28), ex);
throw new FirmaXMLError(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR28));
}
/**
* Valida XAdES-BES.
* @return true Si la fecha de firma es anterior a la actual, si el certificado firmante es el que
* aparece en el nodo SigningCertificate y si existe al menos un nodo Reference con una URI apuntando al nodo SignedProperties.
* y dicho nodo contiene un attributo de tipo Type con un valor dependiente del esquema
*/
private boolean validarXadesBes(String path, EstructuraFirma estructuraFirma) {
if (estructuraFirma.firma == null) {
esValido = false;
// No se encuentra la firma a validar
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR76));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR76));
return false;
}
// Se valida, si existe, que SigningTime no sea posterior a la fecha actual
Date fechaFirma = obtenerFechaFirma();
if (fechaFirma != null) {
if (fechaFirma.after(new Date(System.currentTimeMillis()))) {
esValido = false;
// Firma inv\u00e1lida. La fecha del sello de tiempo del nivel XAdES BES es posterior a la actual
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR157));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR157));
return false;
}
}
// Se obtiene la cadena de certificados contenida en SigningCertificate si existe
ArrayList<DatosX509> certificadosSigning = new ArrayList<DatosX509> ();
DatosX509 datos = new DatosX509();
ArrayList<Element> nodosSigningCertificate = UtilidadTratarNodo.obtenerNodos(estructuraFirma.signedSignatureProperties, null, new NombreNodo(estructuraFirma.esquema, LIBRERIAXADES_SIGNINGCERTIFICATE));
// NodeList nodosSigningCertificate = firmaAValidar.getElementsByTagNameNS(esquema, LIBRERIAXADES_SIGNINGCERTIFICATE);
if (nodosSigningCertificate.size() > 0) {
Node nodoSigningCertificate = nodosSigningCertificate.get(0);
NodeList nodosCert = nodoSigningCertificate.getChildNodes();
int nodosCertSize = nodosCert.getLength();
for(int i = 0; i < nodosCertSize; ++i) {
Node nodoCert = nodosCert.item(i);
if (nodoCert.getNodeType() == Node.ELEMENT_NODE)
{
Element certDigest = (Element)((Element)nodoCert).getElementsByTagNameNS(esquema, LIBRERIAXADES_CERTDIGEST).item(0);
if (certDigest != null) {
NodeList digAlgs = certDigest.getElementsByTagNameNS(SCHEMA_DSIG, LIBRERIAXADES_DIGEST_METHOD);
if (digAlgs != null) {
Element certDigestAlgElement = (Element)digAlgs.item(0);
datos.setAlgMethod(certDigestAlgElement.getAttributes().getNamedItem(ALGORITHM).getNodeValue());
}
NodeList digValues = certDigest.getElementsByTagNameNS(SCHEMA_DSIG, LIBRERIAXADES_DIGESTVALUE);
if (digValues != null) {
Element certDigestValElement = (Element)digValues.item(0);
datos.setDigestValue(certDigestValElement.getFirstChild().getNodeValue());
}
}
Element issuerSerial = (Element)((Element)nodoCert).getElementsByTagNameNS(esquema, LIBRERIAXADES_ISSUER_SERIAL).item(0);
if (issuerSerial != null) {
NodeList issuerVals = issuerSerial.getElementsByTagNameNS(SCHEMA_DSIG, LIBRERIAXADES_X_509_ISSUER_NAME);
if (issuerVals != null) {
Element issuerValElement = (Element)issuerVals.item(0);
String issuerName = issuerValElement.getFirstChild().getNodeValue();
try {
X500Principal prin = new X500Principal(issuerName);
datos.setIssuer(prin.getName());
} catch (IllegalArgumentException ex) {
esValido = false;
// Error en la validación. No se pudo obtener el certificado firmante
resultado.setLog(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_3));
// Error al instanciar la factoría de certificados
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR23), ex);
return false;
} catch (NullPointerException ex) {
esValido = false;
// Error en la validación. No se pudo obtener el certificado firmante
resultado.setLog(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_3));
// Error al instanciar la factoría de certificados
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR23), ex);
return false;
}
}
NodeList serialVals = issuerSerial.getElementsByTagNameNS(SCHEMA_DSIG, LIBRERIAXADES_X_509_SERIAL_NUMBER);
if (serialVals != null) {
Element serialValElement = (Element)serialVals.item(0);
datos.setSerial(new BigInteger(serialValElement.getFirstChild().getNodeValue()));
}
}
certificadosSigning.add(datos);
}
}
}
if (certificadosSigning.size() > 0) { // Si no existe éste nodo, no es preciso validarlo
// Se obtiene la cadena de certificados de KeyInfo, si existe
ArrayList<X509Certificate> certificadosKeyInfo = new ArrayList<X509Certificate> ();
NodeList nodosKeyInfo = firmaAValidar.getElementsByTagNameNS(SCHEMA_DSIG, LIBRERIAXADES_KEY_INFO);
if (nodosKeyInfo.getLength() > 0) {
Element nodoKeyInfo = (Element)nodosKeyInfo.item(0);
// Obtenemos los nodos X509Data
NodeList nodosX509Data = nodoKeyInfo.getElementsByTagNameNS(SCHEMA_DSIG, LIBRERIAXADES_X509_DATA);
int nodosX509DataLenght = nodosX509Data.getLength();
for(int i = 0; i < nodosX509DataLenght; ++i) {
Element nodoX509Data = (Element)nodosX509Data.item(i);
// Obtenemos sus nodos X509Certificate
NodeList x509Cert = nodoX509Data.getElementsByTagNameNS(SCHEMA_DSIG, LIBRERIAXADES_X509_CERTIFICATE);
int x509CertLenght = x509Cert.getLength();
for(int x = 0; x < x509CertLenght; ++x) {
Node nodoX509Certificate = x509Cert.item(x);
try
{
ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decode(nodoX509Certificate.getFirstChild().getNodeValue()));
CertificateFactory cf = CertificateFactory.getInstance(X_509);
certificadosKeyInfo.add((X509Certificate)cf.generateCertificate(bais));
} catch (CertificateException e1) {
esValido = false;
// Error en la validación. No se pudo obtener el certificado firmante
resultado.setLog(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_3));
// Error al instanciar la factoría de certificados
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR23), e1);
return false;
}
}
}
}
// Se obtiene la cadena de certificados de CertificateValues o en su lugar, CompleteCertificateRefs
ArrayList<X509Certificate> certificadosRef = new ArrayList<X509Certificate> ();
NodeList nodosCertValue = firmaAValidar.getElementsByTagNameNS(esquema, CERTIFICATE_VALUES);
if (nodosCertValue.getLength() > 0) {
Element nodoCertValue = (Element)nodosCertValue.item(0);
// Obtenemos los nodos EncapsulatedX509Certificate
NodeList nodosX509Cert = nodoCertValue.getElementsByTagNameNS(esquema, LIBRERIAXADES_X509VALUE);
int nodosX509DataLenght = nodosX509Cert.getLength();
for(int i = 0; i < nodosX509DataLenght; ++i) {
Element nodoX509Cert = (Element)nodosX509Cert.item(i);
try
{
ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decode(nodoX509Cert.getFirstChild().getNodeValue()));
CertificateFactory cf = CertificateFactory.getInstance(X_509);
certificadosRef.add((X509Certificate)cf.generateCertificate(bais));
} catch (CertificateException e1) {
esValido = false;
// Firma inválida. No se pudo obtener el certificado de firma
resultado.setLog(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_3));
// Error al instanciar la factoría de certificados
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR23), e1);
return false;
}
}
} else
{
// Se obtienen los nodos CompleteCertificateRefs
NodeList nodosCompCertRef = firmaAValidar.getElementsByTagNameNS(esquema, COMPLETE_CERTIFICATE_REFS);
if (nodosCompCertRef.getLength() > 0) {
// Obtenemos el nodo CertRefs, que contiene los nodos Cert
Node nodoCertRefs = nodosCompCertRef.item(0).getFirstChild();
NodeList nodosCert = nodoCertRefs.getChildNodes();
int nodosCertSize = nodosCert.getLength();
for(int i = 0; i < nodosCertSize; ++i) {
Node nodoCert = nodosCert.item(i);
String uri = null;
try {
uri = URLDecoder.decode(nodoCert.getAttributes().getNamedItem(URI).getNodeValue(), UTF8);
} catch (UnsupportedEncodingException e) {
// No se puede decodificar la URI a UTF-8 del nodo CertRef para la validación
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR31), e);
esValido = false;
// Firma inválida. No se pudo obtener el certificado firmante
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR3));
} catch (Exception e) {
// No se pudo recuperar la URI del nodo CertDigest
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR65), e);
esValido = false;
// Firma inválida. No se pudo obtener el certificado firmante
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR3));
}
// La uri puede apuntar a un nodo dentro de CertificateValues o a un fichero externo.
// Dado que el flujo impone que no exista dicho nodo, se va a buscar el fichero externo
if (uri != null) {
// Se recoge el destino
if (path != null && !path.endsWith(System.getProperty(FILE_SEPARATOR)))
path = path + System.getProperty(FILE_SEPARATOR);
File certPath = new File(path + uri);
X509Certificate certFile = null;
try {
FileInputStream fis = new FileInputStream(certPath);
try
{
// Se obtiene el certificado en su formato del archivo de la URI
CertificateFactory cf = CertificateFactory.getInstance(X_509);
certFile = (X509Certificate)cf.generateCertificate(fis);
} catch (CertificateException e1) {
// Error al instanciar la factoría de certificados
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR23), e1);
esValido = false;
// Firma inválida. No se pudo obtener el certificado firmante
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR3));
return false;
} finally {
if (fis != null) {
try {
// AppPerfect: Falso positivo
fis.close();
} catch (IOException e) {}
}
}
} catch (FileNotFoundException e) {
// No se pueden encontrar los archivos de certificado para la validación
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR50), e);
esValido = false;
// Firma inválida. No se pudo obtener el certificado firmante
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR3));
return false;
}
// Se toman las variables de la firma para validar que sean correctas
Element certElement = (Element)nodoCert;
Element certDigest = (Element)certElement.getElementsByTagNameNS(esquema, LIBRERIAXADES_CERTDIGEST).item(0); // Sacamos CertDigest
String alg = null;
String digest = null;
String resumenCertificado = CADENA_VACIA;
if (certDigest != null) {
Node algorithm = certDigest.getElementsByTagNameNS(SCHEMA_DSIG, LIBRERIAXADES_DIGEST_METHOD).item(0); // Sacamos DigestAlgorithm
Node value = certDigest.getElementsByTagNameNS(SCHEMA_DSIG, LIBRERIAXADES_DIGESTVALUE).item(0); // Sacamos DigestValue
alg = algorithm.getAttributes().getNamedItem(ALGORITHM).getNodeValue(); // Guardamos el valor del algoritmo
digest = value.getFirstChild().getNodeValue(); // Guardamos valor del digest
// Se comprueba que el valor digest del nodo coincida con el digest de la uri
try
{
MessageDigest haseador = UtilidadFirmaElectronica.getMessageDigest(alg);
if (haseador == null) {
esValido = false;
// No se encontró el algoritmo para calcular el valor del digest del certificado
resultado.setLog(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_16));
// No se puede calcular la huella del certificado para la validación
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR52));
return false;
}
byte[] resumenMensajeByte = haseador.digest(certFile.getEncoded());
resumenCertificado = new String(Base64Coder.encode(resumenMensajeByte));
} catch (CertificateEncodingException e) {
// No se puede calcular el digest del certificado para la validación: XXXX
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR52) +
DOS_PUNTOS_ESPACIO + e.getMessage(), e);
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR52));
return false;
}
if (digest.equals(resumenCertificado)) {
certificadosRef.add(certFile);
} else {
esValido = false;
// No coincide el certificado con el almacenado
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR53));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR53));
return false;
}
} else {
// No se pueden recuperar los nodos de CompleteCertificateRefs
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR66));
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR66));
esValido = false;
return false;
}
} else {
// No se puede continuar la validación dado que hay CertificateRefs sin URI, ni certificateValues
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR83));
esValido = false;
// Firma inválida. No se pudo obtener el certificado firmante
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR3));
return false;
}
}
}
}
// Con ambos resultados (certificadosKeyInfo y certificadosRef) construir una cadena
certificadosRef.addAll(certificadosKeyInfo);
ArrayList<ArrayList<X509Certificate>> certChains = UtilidadCertificados.getCertPathsArray(certificadosRef);
if (certChains.size() > 1) {
// No se pueden validar dos o más cadenas de certificados
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR84));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR84));
return false;
} else {
cadenaCertificados = certChains.get(0);
// Almacenamos la cadena construida
datosFirma.setCadenaFirma(UtilidadCertificados.convertCertPath(cadenaCertificados));
}
// Se compara el certificado firmante (1º en KeyInfo) contra todos los signingCertificate
X509Certificate certFirmante = certificadosKeyInfo.get(0);
String certFirmIssuer = certFirmante.getIssuerX500Principal().getName();
BigInteger certFirmSerial = certFirmante.getSerialNumber();
boolean coincidencia = false;
for (int i = 0; i < certificadosSigning.size(); ++i) {
DatosX509 certAComparar = certificadosSigning.get(i);
if (certFirmIssuer.equals(certAComparar.getIssuer()) &&
certFirmSerial.equals(certAComparar.getSerial())) {
// En caso de que haya alguno coincidente, se busca 1ºKeyInfo/1ºX509Data/X509IssuerSerial
if (nodosKeyInfo.getLength() > 0) {
Element nodoKeyInfo = (Element)nodosKeyInfo.item(0); // Tomamos el primer nodo KeyInfo
Element primerNodoX509Data = (Element)nodoKeyInfo.getElementsByTagNameNS(SCHEMA_DSIG, LIBRERIAXADES_X509_DATA).item(0);
NodeList nodosIssuerSerial = primerNodoX509Data.getElementsByTagNameNS(SCHEMA_DSIG, LIBRERIAXADES_X509_SERIAL_ISSUER);
if (nodosIssuerSerial.getLength() > 0) {
// y si existe, se comprueba que el valor de ese nodo sea el mismo que el indicado en
// el nodo de información "certAComparar"
String issuer = null;
BigInteger serial = null;
Element nodoIssuerSerial = (Element)nodosIssuerSerial.item(0);
NodeList issuerVals = nodoIssuerSerial.getElementsByTagNameNS(SCHEMA_DSIG, LIBRERIAXADES_X_509_ISSUER_NAME);
if (issuerVals != null) {
Element issuerValElement = (Element)issuerVals.item(0);
issuer = issuerValElement.getFirstChild().getNodeValue();
}
NodeList serialVals = nodoIssuerSerial.getElementsByTagNameNS(SCHEMA_DSIG, LIBRERIAXADES_X_509_SERIAL_NUMBER);
if (serialVals != null) {
Element serialValElement = (Element)serialVals.item(0);
serial = new BigInteger(serialValElement.getFirstChild().getNodeValue());
}
if ((certAComparar.getIssuer()).equals(issuer) &&
(certAComparar.getSerial()).equals(serial)) {
// El nodo IssuerSerial es válido
} else {
// No coincide la información del nodo X509IssuerSerial con el certificado de firma
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR85));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR85));
return false;
}
}
}
// Se comprueba que el digest de certFirmante sea igual al encontrado con mismo issuer y serial
// Cálculo del digest del certificado de firma
MessageDigest haseador = UtilidadFirmaElectronica.getMessageDigest(certAComparar.getAlgMethod());
byte[] digestCertFirmante = null;
try {
digestCertFirmante = haseador.digest(certFirmante.getEncoded());
} catch (CertificateEncodingException e) {
// No se puede codificar el certificado firmante para calcular su digest
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR52));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR52));
return false;
}
if (Utilidades.isEqual(digestCertFirmante, Base64Coder.decode(certAComparar.getDigestValue()))) {
// El certificado de firma se corresponde con el nodo SigningCertificate, por lo que es válido
coincidencia = true;
// Se elimina el coincidente para la siguiente validación
certificadosSigning.remove(i);
break;
} else {
// No coinciden los valores de digest del nodo de firma con los de la cadena generada
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR86));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR86));
return false;
}
}
}
// Si no se encontró ninguna coincidencia con el certificado firmante se da la firma por inválida
if (!coincidencia) {
// Incorrectamente identificado el certificado de firma
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR87));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR87));
return false;
}
// Se comprueba que en el resto de SigningCertificate, tenga coincidencia con alguno dentro de las cadenas generadas
int validos = 0;
for (int i = 0; i < certificadosSigning.size(); ++i) {
DatosX509 certAComparar = certificadosSigning.get(i);
for (int x = 0; x < cadenaCertificados.size(); ++x) {
X509Certificate certContenidos = (X509Certificate)cadenaCertificados.get(x);
String certContIssuer = certContenidos.getIssuerX500Principal().getName();
BigInteger certContSerial = certContenidos.getSerialNumber();
if (certContIssuer.equals(certAComparar.getIssuer()) &&
certContSerial.equals(certAComparar.getSerial())) {
// Cálculo del digest del certificado de firma
MessageDigest haseador = UtilidadFirmaElectronica.getMessageDigest(certAComparar.getAlgMethod());
byte[] digestCertContenidos = null;
try {
digestCertContenidos = haseador.digest(certContenidos.getEncoded());
} catch (CertificateEncodingException e) {
// No se puede codificar el certificado contenido en KeyInfo para calcular su digest
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR52));
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_8), e);
return false;
}
if (Utilidades.isEqual(digestCertContenidos, Base64Coder.decode(certAComparar.getDigestValue()))) {
// Certificado válido
validos++;
continue;
} else {
// No coinciden los valores de digest de los nodos SigningCertificate con los de la cadena de firma
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR88));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR88));
return false;
}
}
}
}
if (validos < certificadosSigning.size()) {
// Hay certificados dentro del nodo SigningCertificate sin coincidencia con los certificados de referencia
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR89));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR89));
return false;
}
}
// Se valida que exista un Reference con la URI apuntando al Id de SignedProperties (dependiente del esquema)
NodeList nodosSignedProperties = firmaAValidar.getElementsByTagNameNS(esquema, SIGNED_PROPERTIES);
if (nodosSignedProperties.getLength() == 0) {
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR77));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR77));
return false;
}
Element nodoSignedProperties = (Element)nodosSignedProperties.item(0);
Node signedPropertiesId = nodoSignedProperties.getAttributes().getNamedItem(ID);
if (signedPropertiesId == null) {
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR78));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR78));
return false;
}
String nodoId = signedPropertiesId.getNodeValue();
NodeList references = firmaAValidar.getElementsByTagNameNS(SCHEMA_DSIG, REFERENCE);
int referencesLenght = references.getLength();
if (referencesLenght == 0) {
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR79));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR79));
return false;
}
String tipoEsperado = UtilidadFirmaElectronica.obtenerTipoReference(esquema);
for (int i = 0; i < referencesLenght; i++) {
Element reference = (Element)references.item(i);
String uri = reference.getAttribute(URI);
if (uri == null)
continue;
if (uri.startsWith(ALMOHADILLA))
uri = uri.substring(1);
else
continue;
if (nodoId.equals(uri)) {
Node referenceType = reference.getAttributes().getNamedItem(TYPE);
if (referenceType == null)
continue;
if ((tipoEsperado).equals(referenceType.getNodeValue()))
return true;
}
}
// Si se alcanza éste punto es porque no se encontró ninguna coincidencia
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR80));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR80));
return false;
}
/**
* Valida el sello de tiempo correspondiente a la firma XADES-T
* @return Falso si el sello no está bien formado o si la firma no se corresponde con el elemento
* de firma del documento
*/
private boolean validarSelloTiempoXadesT()
{
TSCliente clienteTSA = null;
NodeList nodosSignatureTimeStamp = null;
NodeList nodesEncapsulatedTimeStamp = null;
Element encapsulatedTimeStampElement = null;
String encapsulatedTS = null;
byte[] timeStampBytes = null ;
TSValidacion tsv1 = null;
DatosSelloTiempo datosSelloTiempo = new DatosSelloTiempo();
String servidorProxy = configuracion.getValor(LIBRERIAXADES_PROXYURL);
String puertoProxy = configuracion.getValor(LIBRERIAXADES_PROXYPORT);
String proxyUser = configuracion.getValor(LIBRERIAXADES_PROXYUSER);
String proxyPass = configuracion.getValor(LIBRERIAXADES_PROXYPASS);
int numeroPuertoProxy = 8080;
try {
numeroPuertoProxy = Integer.parseInt(puertoProxy);
} catch (Exception e) {
log.warn(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_WARN1));
}
try
{
if (configuracion.comparar(LIBRERIAXADES_ISPROXY)) {
System.setProperty("http.proxyHost", servidorProxy);
System.setProperty("http.proxyPort", Integer.toString(numeroPuertoProxy));
if (configuracion.comparar(LIBRERIAXADES_ISPROXYAUTH)) {
Authenticator.setDefault(new SimpleAuthenticator(proxyUser, proxyPass));
}
else {
Authenticator.setDefault(null);
}
}
clienteTSA = new TSCliente(CADENA_VACIA,LIBRERIAXADES_SHA1);
nodosSignatureTimeStamp = firmaAValidar.getElementsByTagNameNS(esquema, LIBRERIAXADES_SIGNATURETIMESTAMP);
if (nodosSignatureTimeStamp.getLength() <= 0) {
// No se puede encontrar el nodo SignatureTimeStamp
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR12));
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_21));
return esValido;
}
Element nodoSigTimeStamp = (Element)nodosSignatureTimeStamp.item(0);
if (esquema.equals(SCHEMA_XADES_122)) {
NodeList nodosInclude = nodoSigTimeStamp.getElementsByTagNameNS(esquema, INCLUDE);
if ( nodosInclude == null ||nodosInclude.getLength() <= 0 || nodosInclude.getLength() > 1) {
// Número de nodos Include inesperado
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR12));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR94) +
ESPACIO + nodosInclude==null?0:nodosInclude.getLength());
return esValido;
}
NamedNodeMap hashAttrb = nodosInclude.item(0).getAttributes();
if (hashAttrb == null ||
hashAttrb.getLength() != 1 ||
hashAttrb.getNamedItem(URI) == null) {
// No se puede recuperar la URI del nodo Include
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR12));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR92));
return esValido;
}
String timeStampUri = hashAttrb.getNamedItem(URI).getNodeValue();
Element nodoReferenciado = UtilidadTratarNodo.getElementById(firmaAValidar, timeStampUri.substring(1));
if (nodoReferenciado == null || !SIGNATURE_VALUE.equals(nodoReferenciado.getLocalName())) {
// No se puede recuperar el nodo SignatureValue con Id XXXX
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR12));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR95) +
ESPACIO + SIGNATURE_VALUE + ESPACIO + timeStampUri);
return esValido;
}
}
// Se valida, si existe, el nodo CanonicalizationMethod
NodeList nodosCanonicalizationMethod = nodoSigTimeStamp.getElementsByTagNameNS(SCHEMA_DSIG, CANONICALIZATION_METHOD);
int numNodosCanonicalization = nodosCanonicalizationMethod.getLength();
if (numNodosCanonicalization > 0) {
Element nodoCanonicalizationMethod = (Element)nodosCanonicalizationMethod.item(0);
String method = nodoCanonicalizationMethod.getAttribute(ALGORITHM);
if (!URL_CANONICALIZATION.equals(method)) {
esValido = false;
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR100) +
ESPACIO + method);
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR100) +
ESPACIO + method);
return esValido;
}
}
// Se chequea la validez del sello de tiempo encapsulado
nodesEncapsulatedTimeStamp = firmaAValidar.getElementsByTagNameNS(esquema, LIBRERIAXADES_ENCTIMESTAMP);
encapsulatedTimeStampElement = (Element)nodesEncapsulatedTimeStamp.item(0);
encapsulatedTS = encapsulatedTimeStampElement.getFirstChild().getNodeValue() ;
timeStampBytes = Base64.decode(encapsulatedTS) ;
byte[] nodeSignatureValue = UtilidadTratarNodo.obtenerByteNodo(firmaAValidar, SCHEMA_DSIG, LIBRERIAXADES_SIGNATUREVALUE);
tsv1 = clienteTSA.validarSelloTiempo(nodeSignatureValue, timeStampBytes);
if(!tsv1.isRespuesta())
esValido = false;
Date fechaSello = tsv1.getFechaDate();
if (fechaSello != null) {
if (fechaSello.after(new Date(System.currentTimeMillis()))) {
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR81));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR81));
return false;
}
}
if (esValido) {
try {
datosSelloTiempo.setTst(tsv1.getTst());
datosSelloTiempo.setFecha(fechaSello);
datosSelloTiempo.setEmisor(tsv1.getEmisor());
datosSelloTiempo.setAlgoritmo(TSPAlgoritmos.getAlgName(tsv1.getSelloAlg()));
datosSelloTiempo.setPrecision(tsv1.getPrecisionLong());
datosSelloTiempo.setTipoSello(TipoSellosTiempo.CLASE_T);
} catch (Exception e) {
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR64));
}
arrayDatosSello.add(datosSelloTiempo);
}
}
catch (NoSuchAlgorithmException e)
{
esValido = false;
// Se ha producido un error al validar XADES-T
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR12));
log.error(e.getMessage());
}
catch (NoSuchProviderException e)
{
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR12));
log.error(e.getMessage());
}
catch (CertStoreException e)
{
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR12));
log.error(e.getMessage());
}
catch (FirmaXMLError e)
{
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR12));
log.error(e.getMessage());
}
catch (TSPException e)
{
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR12));
log.error(e.getMessage());
}
catch (IOException e)
{
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR12));
log.error(e.getMessage());
}
catch (TSClienteError e)
{
String sello = CADENA_VACIA;
if ((tipoDocFirma.getTipoXAdES()).compareTo(EnumFormatoFirma.XAdES_XL)==0)
sello = LIBRERIAXADES_PRIMER;
esValido = false;
// El sello de tiempo no tiene un formato correcto
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR8) + ESPACIO +
sello + I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR9) + ESPACIO +
tipoDocFirma.getTipoXAdES() + ESPACIO +
I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR13));
log.error(e.getMessage());
}
catch (Exception e)
{
String sello = CADENA_VACIA;
if ((tipoDocFirma.getTipoXAdES()).compareTo(EnumFormatoFirma.XAdES_XL)==0)
sello = LIBRERIAXADES_PRIMER;
esValido = false;
// El sello de tiempo no tiene un formato correcto
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR8) + ESPACIO +
sello + I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR9) + ESPACIO +
tipoDocFirma + ESPACIO +
I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR13));
log.error(e.getMessage());
}
finally
{
clienteTSA = null;
nodesEncapsulatedTimeStamp = null;
encapsulatedTimeStampElement = null;
encapsulatedTS = null;
timeStampBytes = null;
tsv1 = null;
}
return esValido;
}
/**
* Valida el sello de tiempo de tipo 1 implícito correspondiente a la firma XADES-X de los esquemas 1.2.2 y 1.3.2
* @return Falso si el sello de tiempo no está bien formado o si la firma no se corresponde con
* los elementos combinados del documento. Los elementos son los siguientes:
* - SignatureValue
* - SignatureTimestamp
* - CompleteCertificateRefs
* - CompleteRevocationRefs
* Opcionalmente en el esquema 1.2.2 y 1.3.2:
* - AttributeCertificateRefs
* - AttributeRevocationRefs
*/
private boolean validarSelloTiempoXadesXTipo1()
{
// Se obtiene el/los nodo/s SigAndRefsTimeStamp. Se validan todos los encontrados
NodeList nodesSigAndRefsTimeStamp = firmaAValidar.getElementsByTagNameNS(esquema, SIG_AND_REFS_TIME_STAMP);
int numSigAndRefs = nodesSigAndRefsTimeStamp.getLength();
if (numSigAndRefs == 0)
return false;
for (int i = 0; i < numSigAndRefs; ++i) {
esValido = validarSegundoSelloTiempo((Element)nodesSigAndRefsTimeStamp.item(i));
if (!esValido)
break;
}
return esValido;
}
/**
* Valida el sello de tiempo de tipo 2 explícito correspondiente a la firma XADES-X de los esquemas 1.2.2 y 1.3.2
* @return Falso si el sello de tiempo no está bien formado o si la firma no se corresponde con
* los elementos combinados del documento. Los elementos son los siguientes:
* - CompleteCertificateRefs
* - CompleteRevocationRefs
* Opcionalmente en el esquema 1.2.2 y 1.3.2:
* - AttributeCertificateRefs
* - AttributeRevocationRefs
*/
private boolean validarSelloTiempoXadesXTipo2()
{
// Se obtiene el/los nodo/s RefsOnlyTimeStamp. Se validan todos los encontrados
NodeList nodesRefsOnlyTimeStamp = firmaAValidar.getElementsByTagNameNS(esquema, REFS_ONLY_TIME_STAMP);
int numRefsOnly = nodesRefsOnlyTimeStamp.getLength();
if (numRefsOnly == 0)
return false;
for (int i = 0; i < numRefsOnly; ++i) {
esValido = validarSegundoSelloTiempo((Element)nodesRefsOnlyTimeStamp.item(i));
if (!esValido)
break;
}
return esValido;
}
/**
* Valida el sello de tiempo de correspondiente a la firma XADES-X de los esquemas 1.2.2 y 1.3.2
* @return Falso si el sello de tiempo no está bien formado o si la firma no se corresponde con
* los elementos combinados del documento.
*/
private boolean validarSegundoSelloTiempo(Element selloTiempo)
{
TSValidacion tsv2 = null;
DatosSelloTiempo datosSelloTiempo = new DatosSelloTiempo();
TipoSellosTiempo tipoSello = TipoSellosTiempo.CLASE_X_TIPO_1;
if (new NombreNodo(esquema, SIG_AND_REFS_TIME_STAMP).equals(
new NombreNodo(selloTiempo.getNamespaceURI(), selloTiempo.getLocalName())))
tipoSello = TipoSellosTiempo.CLASE_X_TIPO_1;
else if (new NombreNodo(esquema, REFS_ONLY_TIME_STAMP).equals(
new NombreNodo(selloTiempo.getNamespaceURI(), selloTiempo.getLocalName())))
tipoSello = TipoSellosTiempo.CLASE_X_TIPO_2;
else {
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR16));
// El sello de tiempo xxx no es un sello válido
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR8)+ ESPACIO +
I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR9) + ESPACIO +
selloTiempo.getLocalName() + ESPACIO + I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR10));
return esValido;
}
// Se obtiene el listado de elementos de un sello de tiempo XAdES X
ArrayList<Element> elementosSelloX = null;
try {
if (TipoSellosTiempo.CLASE_X_TIPO_1.equals(tipoSello))
elementosSelloX = UtilidadXadesX.obtenerListadoXADESX1imp(esquema, firmaAValidar, selloTiempo);
else
elementosSelloX = UtilidadXadesX.obtenerListadoXADESX2exp(esquema, firmaAValidar, selloTiempo);
} catch (BadFormedSignatureException e) {
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR16));
log.error(e.getMessage(), e);
return esValido;
} catch (FirmaXMLError e) {
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR16));
log.error(e.getMessage(), e);
return esValido;
}
if (SCHEMA_XADES_122.equals(esquema)) {
// Se obtienen las Ids de los nodos del sello de tiempo X
ArrayList<String> elementosIdSelloX = UtilidadTratarNodo.obtenerIDs(elementosSelloX);
// Se recogen todos los nodos Include dentro del sello de tiempo XAdES-X
NodeList nodosInclude = selloTiempo.getElementsByTagNameNS(esquema, INCLUDE);
int numNodosInclude = nodosInclude.getLength();
ArrayList<String> urisInclude = new ArrayList<String>(numNodosInclude) ;
Element nodoInclude = null;
// Se recoge la URI de los nodos Include por orden de aparición
for (int j = 0; j < numNodosInclude ; ++j) {
if (j == 0)
nodoInclude = (Element)selloTiempo.getFirstChild();
else
nodoInclude = (Element)nodoInclude.getNextSibling();
if (nodoInclude == null || !INCLUDE.equals(nodoInclude.getLocalName())) {
// No se puede recuperar el nodo Include
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR93));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR93));
return esValido;
}
// Se obtiene la URI del nodo Include
NamedNodeMap atributosNodo = nodoInclude.getAttributes();
if (atributosNodo == null || atributosNodo.getNamedItem(URI) == null) {
// No se puede recuperar la URI del nodo Include
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR92));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR92));
return esValido;
}
String uriInclude = atributosNodo.getNamedItem(URI).getNodeValue();
urisInclude.add(uriInclude);
}
// Comparamos ambos listados
for (int j = 0; j < numNodosInclude; ++j) {
String idUri = urisInclude.get(j).substring(1);
if (!idUri.equals(elementosIdSelloX.get(j))) {
// No se corresponde el orden de los nodos de referencia con el orden esperado según esquema
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR97) +
ESPACIO + idUri + ESPACIO +
I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR98));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR97) +
ESPACIO + idUri + ESPACIO +
I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR98));
return esValido;
}
}
} // Fin de la validación para esquemas 1.2.2
else if (SCHEMA_XADES_132.equals(esquema)) { // Si es esquema 1.3.2, no deben existir nodos Include
// Se recogen los nodos Include si existen
NodeList nodosInclude = selloTiempo.getElementsByTagNameNS(esquema, INCLUDE);
int numNodosInclude = nodosInclude.getLength();
if (numNodosInclude < 0) {
// Inválido, no se pueden validar sellos de este tipo en el esquema 1.3.2
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR101));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR101));
return esValido;
}
}
// Se valida, si existe, el nodo CanonicalizationMethod
NodeList nodosCanonicalizationMethod = selloTiempo.getElementsByTagNameNS(SCHEMA_DSIG, CANONICALIZATION_METHOD);
int numNodosCanonicalization = nodosCanonicalizationMethod.getLength();
if (numNodosCanonicalization > 0) {
Element nodoCanonicalizationMethod = (Element)nodosCanonicalizationMethod.item(0);
String method = nodoCanonicalizationMethod.getAttribute(ALGORITHM);
if (!URL_CANONICALIZATION.equals(method)) {
esValido = false;
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR100) + ESPACIO + method);
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR100) + ESPACIO + method);
return esValido;
}
}
// Se obtiene el array de bytes de los nodos obtenidos
byte[] byteData = null;
try {
byteData = UtilidadTratarNodo.obtenerByte(elementosSelloX);
} catch (FirmaXMLError e) {
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR16));
log.error(e.getMessage(), e);
}
// Se obtiene el array de bytes del nodo EncapsulatedTimeStamp
NodeList nodesEncapsulatedTimeStamp = selloTiempo.getElementsByTagNameNS(esquema, LIBRERIAXADES_ENCTIMESTAMP);
if (nodesEncapsulatedTimeStamp.getLength() != 1) {
// El nodo EncapsulatedTimeStamp dentro del sello de tiempo no existe o no es único
esValido = false;
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_36) + ESPACIO + LIBRERIAXADES_ENCTIMESTAMP +
I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_37) + ESPACIO + nodesEncapsulatedTimeStamp.getLength());
resultado.setLog(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_36) + ESPACIO + LIBRERIAXADES_ENCTIMESTAMP +
I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_37) + ESPACIO + nodesEncapsulatedTimeStamp.getLength());
return esValido;
}
Element encapsulatedTimeStampElement = (Element)nodesEncapsulatedTimeStamp.item(0);
String encapsulatedTS = encapsulatedTimeStampElement.getFirstChild().getNodeValue();
byte [] timeStampBytes = Base64.decode(encapsulatedTS);
// Se comparan los resultados obtenidos
try {
tsv2 = TSCliente.validarSelloTiempo(byteData, timeStampBytes);
} catch (NoSuchAlgorithmException e) {
esValido = false;
// Se ha producido un error al validar XADES-XL
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR16));
log.error(e.getMessage(), e);
return esValido;
} catch (NoSuchProviderException e) {
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR16));
log.error(e.getMessage(), e);
return esValido;
} catch (CertStoreException e) {
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR16));
log.error(e.getMessage(), e);
return esValido;
} catch (TSPException e) {
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR16));
log.error(e.getMessage(), e);
return esValido;
} catch (IOException e) {
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR16));
log.error(e.getMessage(), e);
return esValido;
} catch (TSClienteError e) {
esValido = false;
// El segundo sello de tiempo de la firma XADES-XL no tiene un formato correcto
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR17));
log.error(e.getMessage(), e);
return esValido;
}
if(!tsv2.isRespuesta())
{
esValido = false;
// El segundo sello de tiempo de la firma XADES-XL no es válido
log.info(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR15));
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR15));
return esValido;
}
// Se guardan los resultados obtenidos
Date fechaSello = tsv2.getFechaDate();
if (fechaSello != null) {
if (fechaSello.after(new Date(System.currentTimeMillis()))) {
// La fecha del sello de tiempo es posterior a la actual
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR82));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR82));
return esValido;
}
}
try {
datosSelloTiempo.setFecha(fechaSello);
datosSelloTiempo.setEmisor(tsv2.getEmisor());
datosSelloTiempo.setAlgoritmo(TSPAlgoritmos.getAlgName(tsv2.getSelloAlg()));
datosSelloTiempo.setPrecision(tsv2.getPrecisionLong());
datosSelloTiempo.setTipoSello(tipoSello);
datosSelloTiempo.setTst(tsv2.getTst());
} catch (Exception e) {
// No se pudo generar los datos de la TSA
esValido = false;
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR64), e);
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR64));
return esValido;
}
arrayDatosSello.add(datosSelloTiempo);
return true;
}
/**
* Valida que los certificados almacenados sin firmar se correspondan
* con los resúmenes firmados dentro del documento XADES-C
* En primera instancia, se leen y almacenan todos los nodos de XADES-C
* A continuación se sacan los ficheros .ocs de las URI´s y se validan contra la información de los nodos y/o los nodos CRLRefs
* Posteriormente se sacan los certificados .cer y se valida su encadenamiento, y se chequean contra los nodos
* Finalmente se valida que cada uno de los certificados esté asociado a una respuestaOCSP con estado good y ninguna a revoked
* @return Falso si no se corresponden los dos valores
* @throws FirmaXMLError Si no puede calcular el digest de los certificados almacenados
*/
private boolean validarXadesC(String path, String uriDS, X509Certificate certFirma) throws FirmaXMLError
{
NodeList completeCertificateRefs = null;
NodeList completeRevocationRefs = null;
// Coleccion de datos para los certificados
ArrayList<String> certURI = null;
ArrayList<String> digestAlg = null;
ArrayList<String> digestValue = null;
ArrayList<String> issuerName = null;
ArrayList<String> issuerSerial = null;
// Coleccion de datos para las respuestas OCSP
ArrayList<String> ocspURI = null;
ArrayList<OCSPResponderData> identifierOCSP = null;
ArrayList<Date> identifierTime = null;
ArrayList<String> ocspDigestAlg = null;
ArrayList<String> ocspDigestValue = null;
ArrayList<OCSPResp> respuestasOCSP = null; // Almacenará los .ocp recuperados de los ficheros o de los nodos RevocationValues
// Colección de datos para CRLRefs
ArrayList<String> crlURI = null;
ArrayList<String> crlDigestAlg = null;
ArrayList<String> crlDigestValue = null;
ArrayList<String> crlIssuer = null;
ArrayList<Date> crlIssuerTime = null;
ArrayList<BigInteger> crlNumber = null;
ArrayList<X509CRL> crlList = null; // Almacenará los .crl recuperados de los ficheros o de los nodos RevocationValues
// Se mira que exista el nodo CompleteCertificateRefs y el nodo CompleteRevocationRefs
completeCertificateRefs = firmaAValidar.getElementsByTagNameNS(esquema, LIBRERIAXADES_COMPLETECERTIFICATEREFS);
completeRevocationRefs = firmaAValidar.getElementsByTagNameNS(esquema, LIBRERIAXADES_COMPLETEREVOCATIONREFS);
if (completeCertificateRefs.getLength() == 0 || completeRevocationRefs.getLength() == 0) {
log.debug(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_29)); // No se encuentra CompleteCertificateRefs o CompleteRevocationRefs
resultado.setLog(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_29));
esValido = false;
return esValido;
}
// A continuación se sacan los Certificados del nodo CertRefs
Node certRefs = (Node)completeCertificateRefs.item(0).getFirstChild();
// Si ha encontrado el nodo CertRefs, se pasa a capturar su contenido
if (certRefs != null)
{
// Se saca la lista de certificados
NodeList certs = certRefs.getChildNodes();
int l = certs.getLength();
certURI = new ArrayList<String>(l);
digestAlg = new ArrayList<String>(l);
digestValue = new ArrayList<String>(l);
issuerName = new ArrayList<String>(l);
issuerSerial = new ArrayList<String>(l);
for (int i=0; i<l && esValido; i++)
{
// Sacamos los nodos Cert uno por uno
Element certificate = (Element)certs.item(i); // Sacamos cert
if (certificate != null) {
// Obtenemos su URI, si existe, y la almacenamos
String uri = null;
try {
uri = URLDecoder.decode(certificate.getAttributes().getNamedItem(URI).getNodeValue(), UTF8);
} catch (UnsupportedEncodingException e) {
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR30));
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR31));
} catch (Exception e) {
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR65));
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR65));
}
if (uri != null)
certURI.add(uri);
// y del nodo, sacamos su digest y su issuer
Element certDigest = (Element)certificate.getElementsByTagNameNS(esquema, LIBRERIAXADES_CERTDIGEST).item(0); // Sacamos CertDigest
Node algorithm = certDigest.getElementsByTagNameNS(uriDS, LIBRERIAXADES_DIGEST_METHOD).item(0); // Sacamos DigestAlgorithm
Node value = certDigest.getElementsByTagNameNS(uriDS, LIBRERIAXADES_DIGESTVALUE).item(0); // Sacamos DigestValue
digestAlg.add(algorithm.getAttributes().getNamedItem(ALGORITHM).getNodeValue()); // Guardamos el valor del algoritmo
digestValue.add(value.getFirstChild().getNodeValue()); // Guardamos valor del digest
Element issuer = (Element)certificate.getElementsByTagNameNS(esquema, LIBRERIAXADES_ISSUER_SERIAL).item(0); // Sacamos IssuerSerial
Node name = issuer.getElementsByTagNameNS(uriDS, LIBRERIAXADES_X_509_ISSUER_NAME).item(0); // Sacamos el nombre del emisor
Node serial = issuer.getElementsByTagNameNS(uriDS, LIBRERIAXADES_X_509_SERIAL_NUMBER).item(0); // Sacamos el serial del emisor
issuerName.add(name.getFirstChild().getNodeValue()); // Guardmaos el issuerName
issuerSerial.add(serial.getFirstChild().getNodeValue()); // Guardamos el issuerSerialNumber
}
}
}
// A continuación se sacan las referencias OCSP del nodo OCSPRefs
Node ocspRefs = (Node)firmaAValidar.getElementsByTagNameNS(esquema, OCSP_REFS).item(0);
//Node ocspRefs = (Node)completeRevocationRefs.item(0).getFirstChild();
// Si ha encontrado el nodo OCSPRefs, se pasa a capturar su contenido
if (ocspRefs != null && esValido)
{
// Se saca la lista de referencias
NodeList refs = ocspRefs.getChildNodes();
int l = refs.getLength();
ocspURI = new ArrayList<String>(l);
identifierOCSP = new ArrayList<OCSPResponderData>(l);
OCSPResponderData responderData = null;
identifierTime = new ArrayList<Date>(l);
ocspDigestAlg = new ArrayList<String>(l);
ocspDigestValue = new ArrayList<String>(l);
String noURIOCSPidentifier = I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR32) +
ESPACIO + I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR62);
for (int i=0; i<l && esValido; i++)
{
// Sacamos los nodos OCSPRef uno por uno
Element ocspRef = (Element)refs.item(i); // Sacamos OCSPRef
if (ocspRef != null) {
NodeList list = ocspRef.getElementsByTagNameNS(esquema, LIBRERIAXADES_OCSP_IDENTIFIER);
if (list.getLength() != 0) {
// Obtenemos su URI y la almacenamos
try {
ocspURI.add(URLDecoder.decode((((Element)list.item(0)).getAttributes().getNamedItem(URI).getNodeValue()), UTF8));
} catch (UnsupportedEncodingException e) {
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR30));
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR32));
} catch (Exception e) {
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR30));
esValido = false;
resultado.setLog(noURIOCSPidentifier);
}
// y de él, sacamos su OCSPIdentifier y su digest
try {
Element certDigest = (Element)ocspRef.getElementsByTagNameNS(esquema, LIBRERIAXADES_OCSP_IDENTIFIER).item(0); // Sacamos OCSPIdentifier
Node responder = certDigest.getElementsByTagNameNS(esquema, LIBRERIAXADES_RESPONDER_ID).item(0); // Sacamos ResponderId
Node time = certDigest.getElementsByTagNameNS(esquema, LIBRERIAXADES_PRODUCED_AT).item(0); // Sacamos ProducedAt
responderData = new OCSPResponderData();
if (SCHEMA_XADES_111.equals(esquema) || SCHEMA_XADES_122.equals(esquema)) {
responderData.setIdentificador(responder.getFirstChild().getNodeValue()); // Guardamos el responderID
}
else {
Node responderBy = responder.getFirstChild();
if (BY_NAME.equals(responderBy.getLocalName())) {
responderData.setTipoResponder(TIPOS_RESPONDER.BY_NAME);
try {
X500Principal prin = new X500Principal(responderBy.getFirstChild().getNodeValue());
responderData.setIdentificador(prin.getName()); // Guardamos el responderID
} catch (IllegalArgumentException ex) {
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_23));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR27), ex);
return false;
} catch (NullPointerException ex) {
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_23));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR27), ex);
return false;
}
} else if (BY_KEY.equals(responderBy.getLocalName())) {
responderData.setTipoResponder(TIPOS_RESPONDER.BY_KEY);
responderData.setIdentificador(responderBy.getFirstChild().getNodeValue()); // Guardamos el responderID
} else {
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR91));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR91));
return false;
}
}
identifierOCSP.add(responderData);
Date fecha = UtilidadFechas.parseaFechaXML(time.getFirstChild().getNodeValue());
if (fecha != null)
identifierTime.add(fecha); // Guardamos el producedAt
else {
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR63));
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR40));
}
// TODO: el elemento Digest es opcional y podria no aparecer. Corregir (si no aparece no hay validación de la integridad de la respuesta OCSP)
Element ocspDigest = (Element)ocspRef.getElementsByTagNameNS(esquema, LIBRERIAXADES_DIGESTALGVALUE).item(0); // Sacamos DigestAlgAndValue
Node algorithm = ocspDigest.getElementsByTagNameNS(uriDS, LIBRERIAXADES_DIGEST_METHOD).item(0); // Sacamos DigestAlgorithm
Node value = ocspDigest.getElementsByTagNameNS(uriDS, LIBRERIAXADES_DIGESTVALUE).item(0); // Sacamos DigestValue
ocspDigestAlg.add(algorithm.getAttributes().getNamedItem(ALGORITHM).getNodeValue()); // Guardamos el valor del algoritmo
ocspDigestValue.add(value.getFirstChild().getNodeValue()); // Guardamos valor del digest
} catch (Exception ex) {
log.error(es.mityc.firmaJava.libreria.utilidades.I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR66));
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR66));
}
}
else {
log.debug(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR72));
}
}
}
}
// Se obtienen si existen los nodos CRLRefs
Node crlRefs = null;
if (firmaAValidar.getElementsByTagNameNS(esquema, LIBRERIAXADES_CRLREFS) != null) {
crlRefs = (Node)firmaAValidar.getElementsByTagNameNS(esquema, CRL_REFS).item(0);
}
// Si ha encontrado el nodo CRLRefs, se pasa a capturar su contenido
if (crlRefs != null && esValido)
{
// Se saca la lista de crl
NodeList crls = crlRefs.getChildNodes();
int l = crls.getLength();
crlURI = new ArrayList<String>(l);
crlDigestAlg = new ArrayList<String>(l);
crlDigestValue = new ArrayList<String>(l);
crlIssuer = new ArrayList<String>(l);
crlIssuerTime = new ArrayList<Date>(l);
crlNumber = new ArrayList<BigInteger>(l);
for (int i=0; i<l; i++)
{
// Sacamos los nodos crl uno por uno
Element crl = (Element)crls.item(i); // Sacamos CRLRef
// y del nodo, sacamos su digest y su issuer
if (crl != null) {
Element crlDigest = (Element)crl.getElementsByTagNameNS(esquema, LIBRERIAXADES_DIGESTALGVALUE).item(0); // Sacamos DigestAlgAndValue
Node algorithm = crlDigest.getElementsByTagNameNS(uriDS, LIBRERIAXADES_DIGEST_METHOD).item(0); // Sacamos DigestAlgorithm
Node value = crlDigest.getElementsByTagNameNS(uriDS, LIBRERIAXADES_DIGESTVALUE).item(0); // Sacamos DigestValue
crlDigestAlg.add(algorithm.getAttributes().getNamedItem(ALGORITHM).getNodeValue()); // Guardamos el valor del algoritmo
crlDigestValue.add(value.getFirstChild().getNodeValue()); // Guardamos valor del digest
Element identifier = (Element)crl.getElementsByTagNameNS(esquema, LIBRERIAXADES_CRLIDENTIFIER).item(0); // Sacamos IssuerIdentifier
try {
crlURI.add(URLDecoder.decode(identifier.getAttributes().getNamedItem(URI).getNodeValue(), UTF8)); // Se obtiene la URI
} catch (UnsupportedEncodingException e) {
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR30));
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR33));
} catch (Exception e) {
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR30));
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR32));
}
try {
Node issuer = identifier.getElementsByTagNameNS(esquema, LIBRERIAXADES_ISSUER).item(0); // Sacamos el Issuer
Node issuerTime = identifier.getElementsByTagNameNS(esquema, LIBRERIAXADES_ISSUERTIME).item(0); // Sacamos el tiempo del Issuer
Node number = identifier.getElementsByTagNameNS(esquema, LIBRERIAXADES_NUMBER).item(0); // Sacamos el numero de la CRL (opcional)
String crlIssuerName = issuer.getFirstChild().getNodeValue();
try {
X500Principal prin = new X500Principal(crlIssuerName);
crlIssuer.add(prin.getName());
} catch (IllegalArgumentException ex) {
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_23));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR44), ex);
return false;
} catch (NullPointerException ex) {
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_23));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR44), ex);
return false;
}
Date fecha = UtilidadFechas.parseaFechaXML(issuerTime.getFirstChild().getNodeValue());
if (fecha != null)
crlIssuerTime.add(fecha); // Guardamos el issuerTime
else {
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR63));
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR40));
}
if (number != null) {
crlNumber.add(new BigInteger(number.getFirstChild().getNodeValue())); // Guardamos el Number
}
} catch (Exception ex) {
log.error(es.mityc.firmaJava.libreria.utilidades.I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR66));
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR66));
}
}
}
}
NodeList certificateValues = null;
NodeList revocationValues = null;
certificateValues = firmaAValidar.getElementsByTagNameNS(esquema, CERTIFICATE_VALUES);
revocationValues = firmaAValidar.getElementsByTagNameNS(esquema, REVOCATION_VALUES);
// Si existen los nodos, se valida como XAdES-XL
boolean certsYOcspInterno = false;
if (certificateValues.getLength() != 0 && revocationValues.getLength() != 0) {
certsYOcspInterno = true;
}
// Se construye la ruta donde se encuentran los archivos
if (path != null && !path.endsWith(System.getProperty(FILE_SEPARATOR)))
path = path + System.getProperty(FILE_SEPARATOR);
// Se valida CompleteRevocationCerts, en primer lugar los nodos OCSPRef
if (ocspDigestValue != null && ocspDigestValue.size() != 0 && esValido) {
int ocspNum = ocspDigestValue.size();
OCSPResponderData responderData = null;
byte[] respuesta = null;
RespuestaOCSP respuestaOCSP = null;
respuestasOCSP = new ArrayList<OCSPResp>(ocspNum);
for (int x=0; x < ocspNum && esValido; ++x) {
if (certsYOcspInterno) {
Element ocsp = null;
if (ocspURI != null && ocspURI.size() == ocspNum)
ocsp = UtilidadTratarNodo.getElementById(firmaAValidar.getElementsByTagNameNS(esquema, LIBRERIAXADES_OCSPVALUE), ocspURI.get(x).substring(1));
if (ocsp == null) {
ocsp = buscarRevocationValueOCSP(ocspDigestValue.get(x), ocspDigestAlg.get(x));
if (ocsp == null) {
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_8));
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR34));
return esValido;
} else
respuesta = Base64.decode(ocsp.getFirstChild().getNodeValue());
} else {
respuesta = Base64.decode(ocsp.getFirstChild().getNodeValue());
}
}
else if (ocspURI != null && ocspURI.size() == ocspNum) {
String pathFicheroOCSP = null;
try {
pathFicheroOCSP = URLDecoder.decode(ocspURI.get(x),UTF8);
} catch (UnsupportedEncodingException e1) {
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR35),e1);
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR35));
} catch (Exception e) {
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR30));
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR32));
}
File ocspFile = new File(path + pathFicheroOCSP);
FileInputStream fis = null;
try {
fis = new FileInputStream(ocspFile);
respuesta = new byte[(int)ocspFile.length()];
fis.read(respuesta);
} catch (FileNotFoundException e) {
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_8),e);
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR36));
} catch (IOException e) {
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_8),e);
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR37));
} finally {
if (fis != null)
try {
fis.close();
} catch (IOException e) {
}
}
} else {
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_8));
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR36));
}
// Se valida el Digest
String digestOCSPResponse = null;
if (esValido) {
try
{
MessageDigest resumenCertificadoTemp = MessageDigest.getInstance(SHA_1); // TODO: Sacar el algoritmo del nodo
byte[] resumenMensajeByte = resumenCertificadoTemp.digest(respuesta);
digestOCSPResponse = new String(Base64Coder.encode(resumenMensajeByte));
}
catch (NoSuchAlgorithmException nsae)
{
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_20));
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_20));
}
if (!((ocspDigestValue.get(x)).equals(digestOCSPResponse))) {
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR38));
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR38));
}
}
// Reconstruimos la respuesta
OCSPResp resp = null;
if (esValido) {
try {
resp = new OCSPResp(respuesta);
} catch (IOException e) {
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_8),e);
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR37));
}
BasicOCSPResp respuestaBasica = null;
respuestaOCSP = new RespuestaOCSP();
Date tiempoRespuesta = null;
try {
try {
respuestaBasica = (BasicOCSPResp)resp.getResponseObject();
} catch (ClassCastException e) {
continue;
}
respuestaOCSP.setNroRespuesta(resp.getStatus());
tiempoRespuesta =respuestaBasica.getProducedAt();
//respuestaOCSP.setTiempoRespuesta(respuestaBasica.getProducedAt());
ResponderID respID = respuestaBasica.getResponderId().toASN1Object();
respuestaOCSP.setResponder(respID);
respuestaBasica.getResponses();
} catch (OCSPException e) {
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_8),e);
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR37));
}
// y validamos responderId y producedAt
responderData = identifierOCSP.get(x);
if (!((responderData.getIdentificador()).equals(respuestaOCSP.getValorResponder()))) {
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR39));
}
// Si estamos en el esquema 1.3.2 o superior se debe validar el tipo de responder
if (!(SCHEMA_XADES_111.equals(esquema) || SCHEMA_XADES_122.equals(esquema)))
if (!(responderData.getTipoResponder()).equals(respuestaOCSP.getTipoResponder())) {
esValido = false;
// No coincide el tipo de responder del servidor OCSP con el almacenado
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR112));
}
if (!((identifierTime.get(x)).equals(tiempoRespuesta))) {
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR40));
}
// Se almacena la respuesta para validar los certificados
if (esValido) {
respuestasOCSP.add(resp);
}
}
}
}
// Se valida CRLRefs
if (crlDigestValue != null && crlDigestValue.size() != 0 && esValido) {
int crlNum = crlDigestValue.size();
byte[] crl = null;
X509CRL x509CRL = null;
crlList = new ArrayList<X509CRL>(crlNum);
for (int x=0; x < crlNum && esValido; ++x) {
if (certsYOcspInterno) {
Element crlValue = null;
if (crlURI != null && crlURI.size() == crlNum)
crlValue = UtilidadTratarNodo.getElementById(firmaAValidar.getElementsByTagNameNS(esquema, LIBRERIAXADES_CRLVALUE), crlURI.get(x).substring(1));
if (crlValue == null) {
crlValue = buscarRevocationValueCRL(crlDigestValue.get(x), crlDigestAlg.get(x));
if (crlValue == null) {
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_8));
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR41));
return esValido;
} else
crl = Base64.decode(crlValue.getFirstChild().getNodeValue());
} else
crl = Base64.decode(crlValue.getFirstChild().getNodeValue());
}
else if (crlURI != null && crlURI.size() == crlNum) {
File crlFile = new File(path + crlURI.get(x));
BufferedInputStream fis = null;
try {
fis = new BufferedInputStream(new FileInputStream(crlFile));
crl = new byte[(int)crlFile.length()];
fis.read(crl);
} catch (FileNotFoundException e) {
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_8),e);
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR42));
} catch (IOException e) {
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_8),e);
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR42));
} finally {
if (fis != null)
try {
fis.close();
} catch (IOException e) {
}
}
} else {
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_8));
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR42));
}
// Se valida el Digest
String digestCRLResponse = null;
if (esValido) {
try
{
MessageDigest resumenCRLTemp = MessageDigest.getInstance(SHA_1); // TODO: Sacar el algoritmo del nodo
byte[] resumenMensajeByte = resumenCRLTemp.digest(crl);
digestCRLResponse = new String(Base64Coder.encode(resumenMensajeByte));
}
catch (NoSuchAlgorithmException nsae)
{
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_20));
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_20));
}
if (!((crlDigestValue.get(x)).equals(digestCRLResponse))) {
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR43));
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR43));
}
}
// Reconstruimos la crl
if (esValido) {
ByteArrayInputStream bais = new ByteArrayInputStream(crl);
CertificateFactory certificatefactory;
try {
certificatefactory = CertificateFactory.getInstance(X_509);
x509CRL = (X509CRL)certificatefactory.generateCRL(bais);
} catch (CertificateException e) {
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_8),e);
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR23));
} catch (CRLException e) {
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_8),e);
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR44));
}
if (x509CRL != null) {
// Se valida issuer
if (!((crlIssuer.get(x)).equals(x509CRL.getIssuerX500Principal().getName()))) {
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR45));
}
// Se valida issuerTime
Date time = x509CRL.getThisUpdate();
if (!((crlIssuerTime.get(x)).equals(time))) {
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR46));
}
// se valida el número de CRL
BigInteger numeroNodo = crlNumber.get(x); // Se recupera el numero de CRL escrito en el nodo
// Si existe el nodo number (opcional), se saca el número de CRL contenido en la URI
if (numeroNodo != null) {
BigInteger numeroRecuperado = null;
DERInteger derInt = null;
// AppPerfect: Falso positivo
ASN1InputStream ais = new ASN1InputStream(x509CRL.getExtensionValue(CRL_NUMBER_OID));
try {
ais = new ASN1InputStream(((DEROctetString)ais.readObject()).getOctets());
derInt = (DERInteger)ais.readObject();
} catch (IOException e) {
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR47));
}
numeroRecuperado = derInt.getValue();
if (!((numeroNodo).equals(numeroRecuperado))) {
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR48));
}
}
}
}
// Se almacena la lista para validar los certificados
if (esValido)
crlList.add(x509CRL);
}
}
// Se valida CompleteCertificateRefs
int numCert = digestValue.size();
ArrayList<X509Certificate> certsDeURI = new ArrayList<X509Certificate> ();
X509Certificate certificado = null;
File certFile = null;
X509Certificate certRaiz = null;
for (int x=0; x < numCert && esValido; ++x) {
if (certsYOcspInterno) {
// El nodo referenciado por la URI puede estar en todo el documento
Element certValue = null;
if (certURI != null && certURI.size() == numCert)
certValue = UtilidadTratarNodo.getElementById(firmaAValidar.getElementsByTagNameNS(esquema, LIBRERIAXADES_X509VALUE), certURI.get(x).substring(1));
if (certValue == null) {
certValue = buscarCertificateValue(issuerName.get(x), new BigInteger(issuerSerial.get(x)));
if (certValue == null) {
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_8));
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR49));
return esValido;
}
}
try {
ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decode(certValue.getFirstChild().getNodeValue()));
CertificateFactory cf = CertificateFactory.getInstance(X_509);
certificado = (X509Certificate)cf.generateCertificate(bais);
} catch (CertificateException e1) {
// Error al instanciar la factoría de certificados
throw new FirmaXMLError(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR23));
}
}
else if (certURI != null && certURI.size() == numCert) {
String pathUri = null;
try {
pathUri = URLDecoder.decode(certURI.get(x),UTF8);
} catch (UnsupportedEncodingException e2) {
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR30),e2);
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR30));
}
certFile = new File(path + pathUri);
try {
FileInputStream fis = new FileInputStream(certFile);
try
{
// Se obtiene el certificado en su formato del archivo de la URI
CertificateFactory cf = CertificateFactory.getInstance(X_509);
certificado = (X509Certificate)cf.generateCertificate(fis);
} catch (CertificateException e1) {
// Error al instanciar la factoría de certificados
throw new FirmaXMLError(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR23));
} finally {
if (fis != null) {
try {
// AppPerfect: Falso positivo
fis.close();
} catch (IOException e) {
}
}
}
} catch (FileNotFoundException e) {
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_8),e);
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR50));
}
} else {
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_8));
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR49));
}
// Se comprueba que el valor digest del nodo coincida con el digest de la uri
String resumenCertificado =CADENA_VACIA;
try
{
MessageDigest resumenCertificadoTemp = UtilidadFirmaElectronica.getMessageDigest(digestAlg.get(x));
if (resumenCertificadoTemp == null) {
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_16));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR52));
return false;
}
byte[] resumenMensajeByte = resumenCertificadoTemp.digest(certificado.getEncoded());
resumenCertificado = new String(Base64Coder.encode(resumenMensajeByte));
} catch (CertificateEncodingException e) {
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_23) + e.getMessage());
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR52));
}
if (!(digestValue.get(x).equals(resumenCertificado))) {
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR53));
}
// Se comprueba que coincidan los numeros de serie
if (!(issuerSerial.get(x).equals(certificado.getSerialNumber().toString()))) {
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR54));
}
if (esValido) {
certsDeURI.add(certificado);
if (certificado.getSubjectX500Principal().equals(certificado.getIssuerX500Principal()))
certRaiz = certificado;
}
}
// Se valida que los certificados se correspondan con la cadena de firma
// Chequea issuer y serial, y posteriormente digest, excluyendo el primero (certificado de firma)
cadenaCertificados.addAll(certsDeURI);
ArrayList<ArrayList<X509Certificate>> certChains = UtilidadCertificados.getCertPathsArray(cadenaCertificados);
if (certChains.size() > 1) {
// No se pueden validar dos o más cadenas de certificados
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR84));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR84));
return false;
} else {
cadenaCertificados = certChains.get(0);
}
Iterator<X509Certificate> certGenIter = cadenaCertificados.iterator();
ArrayList<X509Certificate> cadenaClon = new ArrayList<X509Certificate> (cadenaCertificados);
// Excluimos el primero
cadenaClon.remove(0);
certGenIter.next();
int certRefLenght = digestValue.size();
while (certGenIter.hasNext())
{
X509Certificate certAValidar = (X509Certificate)certGenIter.next();
for (int i=0; i < certRefLenght && esValido; i++)
{
String issuer = issuerName.get(i);
BigInteger serial = new BigInteger(issuerSerial.get(i));
String alg = digestAlg.get(i);
byte[] value = Base64Coder.decode(digestValue.get(i));
if ((issuer).equals(certAValidar.getIssuerX500Principal().getName()) &&
(serial).equals(certAValidar.getSerialNumber())) {
// Cálculo del digest del certificado de firma
MessageDigest haseador = UtilidadFirmaElectronica.getMessageDigest(alg);
byte[] digestCert = null;
try {
digestCert = haseador.digest(certAValidar.getEncoded());
} catch (CertificateEncodingException e) {
// No se puede codificar un certificado de la cadena para calcular su digest
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR52));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR23), e);
return false;
}
if (Utilidades.isEqual(digestCert, value)) {
// Coincidencia, se saca el certificado de la lista
cadenaClon.remove(certAValidar);
break;
}
}
}
}
if (cadenaClon.size() > 0) {
// Invalido
// No se puede validar un certificado de la cadena por falta de referencias
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR67));
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR67));
return false;
}
// Se almacena la cadena de certificados validada
datosFirma.setCadenaFirma(UtilidadCertificados.convertCertPath(cadenaCertificados));
// Se continua con la validación de los certificados a traves de CompleteRevocationRefs
// if ((esValido) && (certRaiz == null)) {
// log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR55));
// esValido = false;
// resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR55));
// }
if (esValido)
{
int numCadenaCerts = cadenaCertificados.size();
for (int i = 0; i < numCadenaCerts; i++ ) {
X509Certificate certAValidar = (X509Certificate) cadenaCertificados.get(i);
X509Certificate certIssuer = null;
if (i < (numCadenaCerts - 1))
certIssuer = (X509Certificate) cadenaCertificados.get(i+1);
else
certIssuer = certAValidar;
CertificateID certificadoId = null;
try {
certificadoId = new CertificateID(CertificateID.HASH_SHA1, certIssuer, certAValidar.getSerialNumber());
} catch (OCSPException ex) {
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_16) + ex.getMessage());
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR56));
break;
}
// Por cada certificado se comprueba que exista al menos una respuestaOCSP good y ninguna revoked
// o que el certificado no exista en ninguna lista de revocación de su mismo issuer
// o no exista RevocationRef asociada
int good = 0;
int revoked = 0;
DatosOCSP datosOCSP = null;
DatosCRL datosCRL = null;
BasicOCSPResp basicOcsp = null;
if (respuestasOCSP != null) { // Se comprueba que la lista de respuestasOCSP no sea nula
Iterator<OCSPResp> itRespOCSP = respuestasOCSP.iterator();
boolean hasNext = itRespOCSP.hasNext();
while (hasNext && esValido) {
OCSPResp respuestaOCSP = itRespOCSP.next();
try {
basicOcsp = (BasicOCSPResp)respuestaOCSP.getResponseObject();
} catch (OCSPException e) {
// No se puede reconstruir la respuesta básica a partir de la respuesta OCSP leída
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR113), e);
break;
}
hasNext = itRespOCSP.hasNext();
SingleResp[] singleResps = basicOcsp.getResponses();
int numSingleResps = singleResps.length;
for (int j = 0; j < numSingleResps; ++j) {
if (certificadoId.equals(singleResps[j].getCertID())) {
Object obj = singleResps[j].getCertStatus();
if (obj == null) {
good++;
datosOCSP = new DatosOCSP();
datosOCSP.setResponderId(basicOcsp.getResponderId().toASN1Object());
datosOCSP.setCertConsultado(certAValidar.getSubjectX500Principal().toString());
datosOCSP.setFechaConsulta(basicOcsp.getProducedAt());
datosOCSP.setRespuestaOCSP(respuestaOCSP);
arrayDatosOCSP.add(datosOCSP);
}
else if (obj instanceof RevokedStatus) {
revoked++;
break;
}
}
}
if (revoked > 0)
break;
}
}
if (crlList != null) { // Se comprueba que la lista de CRL no sea nula
Iterator<X509CRL> itCRLList = crlList.iterator();
boolean hasNext = itCRLList.hasNext();
while (hasNext && esValido) {
X509CRL x509CRL = itCRLList.next();
hasNext = itCRLList.hasNext();
// Si el certificado tiene el mismo issuer que la CRL
if ((x509CRL.getIssuerX500Principal().getName()).equals(certIssuer.getSubjectX500Principal().getName())) {
// se comprueba que el certificado no esté dentro de la lista
if (x509CRL.getRevokedCertificate(certificado) == null) {
good++;
datosCRL = new DatosCRL();
datosCRL.setIssuer(x509CRL.getIssuerX500Principal().getName());
datosCRL.setFechaEmision(x509CRL.getThisUpdate());
datosCRL.setFechaCaducidad(x509CRL.getNextUpdate());
datosCRL.setX509CRL(x509CRL);
arrayDatosCRL.add(datosCRL);
} else {
revoked++;
break;
}
}
}
if (revoked > 0)
break;
}
if ((revoked == 0) && (good == 0)) {
if (!((certAValidar.getSubjectDN()).equals(certAValidar.getIssuerDN()))) {
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR67));
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR67));
break;
}
} else {
if ((revoked > 0) || (good == 0)) {
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR57));
esValido = false;
resultado.setLog(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR57));
break;
}
}
}
}
//TODO: si la firma es XL comprobar que el primer certificado en certificateValues es igual que el firmante
return esValido;
}
/**
* Busca en CertificateValues un certificado encapsulado con el mismo issuer y serial Number que el parametrizado
*
* @param certIssuer
* @param serialNumber
* @return Si se encontró coincidencia, se devuelve el elemento EncapsulatedX509Certificate. En caso contrario, un valor nulo
*/
private Element buscarCertificateValue(String certIssuer, BigInteger serialNumber) {
// Se busca el nodo CertificateValues para obtener sus nodos hijo
NodeList certificateValuesNodeList = firmaAValidar.getElementsByTagNameNS(esquema, CERTIFICATE_VALUES);
int certLength = certificateValuesNodeList.getLength();
for (int i = 0; i < certLength; ++i) {
Element certificateValuesElement = (Element) certificateValuesNodeList.item(i);
NodeList certificados = certificateValuesElement.getChildNodes();
int certificadosLength = certificados.getLength();
for (int j = 0; j < certificadosLength; ++j) {
Element certElement = (Element) certificados.item(j);
if (!(new NombreNodo(esquema, ENCAPSULATED_X_509_CERTIFICATE).equals(
new NombreNodo(certElement.getNamespaceURI(), certElement.getLocalName())))) {
// Al menos existe un nodo hijo de CertificateValues que no es del tipo EncapsulatedX509Certificate
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR114));
return null;
}
// Se obtiene su valor para recontruir el certificado
X509Certificate certificado = null;
String digest = certElement.getFirstChild().getNodeValue();
if (digest != null) {
byte[] data = null;
try {
data = Base64Coder.decode(digest);
} catch (IllegalArgumentException ex) {
// Contenido base64 de EncapsulatedX509Certificate inválido
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR115), ex);
break;
}
ByteArrayInputStream bais = new ByteArrayInputStream(data);
CertificateFactory cf = null;
try {
cf = CertificateFactory.getInstance(X_509);
certificado = (X509Certificate)cf.generateCertificate(bais);
} catch (CertificateException e) {
// No se pudo generar el certificado a partir del Digest EncapsulatedX509Certificate leído
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR116), e);
break;
}
} else {
// No se pudo recuperar el contenido del nodo EncapsulatedX509Certificate
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR117));
return null;
}
String issuerLeido = certificado.getIssuerX500Principal().getName();
BigInteger serialNumberLeido = certificado.getSerialNumber();
// Si coinciden los valores, se devuelve el EncapsulatedX509Certificate encontrado
if (certIssuer.equals(issuerLeido) && serialNumber.equals(serialNumberLeido))
return certElement;
}
}
return null;
}
/**
* Busca en RevocationValues una respuesta OCSP encapsulada que de el mismo valor de digest
*
* @param digest .- Digest que debe resultar del nodo a buscar
* @param method .- Algoritmo a utilizar para el cálculo del digest
* @return Si se encontró coincidencia, se devuelve el elemento EncapsulatedOCSPValue. En caso contrario, un valor nulo
*/
private Element buscarRevocationValueOCSP(String digest, String method) {
// Se busca el nodo RevocationValues
NodeList revocationValuesNodeList = firmaAValidar.getElementsByTagNameNS(esquema, REVOCATION_VALUES);
int revocationLength = revocationValuesNodeList.getLength();
for (int i = 0; i < revocationLength; ++i) {
// Se obtienen los hijos de OCSPValues para cada RevocationValue encontrado
Element revocationValuesElement = (Element) revocationValuesNodeList.item(i);
NodeList ocspValues = revocationValuesElement.getElementsByTagNameNS(esquema, OCSP_VALUES);
if (ocspValues.getLength() != 1) {
// El nodo OCSPValues no existe o no es único
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_36) + ESPACIO + OCSP_VALUES + ESPACIO +
I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_37) + ESPACIO + ocspValues.getLength());
return null;
}
NodeList respuestasOCSP = ocspValues.item(0).getChildNodes();
int respuestasOCSPLength = respuestasOCSP.getLength();
for (int j = 0; j < respuestasOCSPLength; ++j) {
// Se obtienen las respuestas OCSP encapsuladas y se calcula su Digest
Element respuestaOCSPElement = (Element) respuestasOCSP.item(j);
if (!(new NombreNodo(esquema, ENCAPSULATED_OCSP_VALUE).equals(
new NombreNodo(respuestaOCSPElement.getNamespaceURI(), respuestaOCSPElement.getLocalName())))) {
// Al menos existe un nodo hijo de OCSPValues que no es del tipo EncapsulatedOCSPValue
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR118));
return null;
}
byte[] data = null;
String encapsulatedValue = respuestaOCSPElement.getFirstChild().getNodeValue();
if (encapsulatedValue != null) {
try {
data = Base64Coder.decode(encapsulatedValue);
} catch (IllegalArgumentException ex) {
// Contenido base64 de EncapsulatedOCSPValue inválido
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR119), ex);
break;
}
} else {
// No se pudo recuperar el contenido de EncapsulatedOCSPValue
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR120));
return null;
}
// Se calcula el digest con el mismo algoritmo que el OCSP buscado
MessageDigest resumenTemp = UtilidadFirmaElectronica.getMessageDigest(method);
byte[] resumenMensajeByte = resumenTemp.digest(data);
String digestLeido = new String(Base64Coder.encode(resumenMensajeByte));
// Si coinciden los valores, se devuelve el nodo encontrado
if (digest.equals(digestLeido))
return respuestaOCSPElement;
}
}
return null;
}
/**
* Busca en RevocationValues una CRL encapsulada con el mismo digest
*
* @param digest .- Digest que debe resultar del nodo a buscar
* @param method .- Algoritmo a utilizar para el cálculo del digest
* @return Si se encontró coincidencia, se devuelve el elemento EncapsulatedCRLValue. En caso contrario, un valor nulo
*/
private Element buscarRevocationValueCRL(String digest, String method) {
// Se busca el nodo RevocationValues
NodeList revocationValuesNodeList = firmaAValidar.getElementsByTagNameNS(esquema, REVOCATION_VALUES);
int revocationLength = revocationValuesNodeList.getLength();
for (int i = 0; i < revocationLength; ++i) {
// Se obtienen los hijos de CRLValues para cada RevocationValue encontrado
Element revocationValuesElement = (Element) revocationValuesNodeList.item(i);
NodeList crlValues = revocationValuesElement.getElementsByTagNameNS(esquema, CRL_VALUES);
if (crlValues.getLength() != 1) {
// El nodo CRLValues no existe o no es único
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_36) + ESPACIO + CRL_VALUES + ESPACIO +
I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_37) + ESPACIO + crlValues.getLength());
return null;
}
NodeList crls = crlValues.item(0).getChildNodes();
int crlsLength = crls.getLength();
for (int j = 0; j < crlsLength; ++j) {
// Se obtienen las listas de revocación y se reconstruyen para calcular su Digest
Element crlElement = (Element) crls.item(j);
if (!(new NombreNodo(esquema, ENCAPSULATED_CRL_VALUE).equals(
new NombreNodo(crlElement.getNamespaceURI(), crlElement.getLocalName())))) {
// Al menos existe un nodo hijo de CRLValues que no es del tipo EncapsulatedCRLValue
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR121));
return null;
}
// Se obtiene el valor de la CRL
byte[] data = null;
String encapsulatedValue = crlElement.getFirstChild().getNodeValue();
if (encapsulatedValue != null) {
try {
data = Base64Coder.decode(encapsulatedValue);
} catch (IllegalArgumentException ex) {
// Contenido base64 de EncapsulatedCRLValue inválido
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR122), ex);
break;
}
} else {
// No se pudo recuperar el contenido de EncapsulatedCRLValue
log.error(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR123));
return null;
}
// Se calcula el digest con el mismo algoritmo que la CRL buscada
MessageDigest resumenTemp = UtilidadFirmaElectronica.getMessageDigest(method);
byte[] resumenMensajeByte = resumenTemp.digest(data);
String digestLeido = new String(Base64Coder.encode(resumenMensajeByte));
// Si coinciden los valores, se devuelve el nodo encontrado
if (digest.equals(digestLeido))
return crlElement;
}
}
return null;
}
/**
* Indica el tipo de firma que tiene el documento
* @return El nombre del tipo de firma
*/
private DatosTipoFirma tipoFirma(Element firma, String esquemaXAdES) throws BadFormedSignatureException
{
DatosTipoFirma datosTipoFirma = new DatosTipoFirma();
boolean esXAdES_C = false;
boolean esXAdES_X = false;
// Tomaremos que, por defecto, la fima es de tipo XAdES-BES (a continuación se valida que así sea)
datosTipoFirma.setTipoXAdES(EnumFormatoFirma.XAdES_BES);
// Se comprueba que la firma sea XAdES-BES buscando el nodo QualifyingProperties
ArrayList<Element> nodosObject = UtilidadTratarNodo.obtenerNodos(firma, null, new NombreNodo(SCHEMA_DSIG, OBJECT));
Iterator<Element> itObject = nodosObject.iterator();
int numQualifyingProperties = 0;
while (itObject.hasNext()) {
ArrayList<Element> nodosQualifyingProperties = UtilidadTratarNodo.obtenerNodos(itObject.next(), null, new NombreNodo(esquemaXAdES, LIBRERIAXADES_QUALIFYING_PROPERTIES));
numQualifyingProperties += nodosQualifyingProperties.size();
}
if (numQualifyingProperties != 1) {
// El nodo QualifyingProperties no existe o no es único
log.error(I18n.getResource(ConstantesXADES.LIBRERIAXADES_FIRMAXML_ERROR_36) +
ConstantesXADES.ESPACIO + ConstantesXADES.LIBRERIAXADES_QUALIFYING_PROPERTIES +
ConstantesXADES.ESPACIO + I18n.getResource(ConstantesXADES.LIBRERIAXADES_FIRMAXML_ERROR_37) +
ConstantesXADES.ESPACIO + numQualifyingProperties);
throw new BadFormedSignatureException(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR124));
}
// Se comprueba que la firma sea XAdES-EPES buscando el nodo SignaturepolicyIdentifier
NodeList nodosSignaturePolicyIdentifier = firma.getElementsByTagNameNS(
esquemaXAdES, ConstantesXADES.SIGNATURE_POLICY_IDENTIFIER);
int numSignaturePolicyIdentifier = nodosSignaturePolicyIdentifier.getLength();
if (numSignaturePolicyIdentifier > 1) {
// El nodo SignaturePolicyIdentifier no es único
log.error(I18n.getResource(ConstantesXADES.LIBRERIAXADES_FIRMAXML_ERROR_36) +
ConstantesXADES.ESPACIO + ConstantesXADES.SIGNATURE_POLICY_IDENTIFIER +
ConstantesXADES.ESPACIO + I18n.getResource(ConstantesXADES.LIBRERIAXADES_FIRMAXML_ERROR_38) +
ConstantesXADES.ESPACIO + numSignaturePolicyIdentifier);
// Firma XAdES-EPES mal formada
throw new BadFormedSignatureException(I18n.getResource(ConstantesXADES.LIBRERIAXADES_VALIDARFIRMA_ERROR125));
} else if (ConstantesXADES.SCHEMA_XADES_111.equals(esquemaXAdES) && numSignaturePolicyIdentifier < 1) {
// No se encuentra el nodo SignaturePolicyIdentifier
log.error(I18n.getResource(ConstantesXADES.LIBRERIAXADES_FIRMAXML_ERROR_33) +
ConstantesXADES.ESPACIO + ConstantesXADES.SIGNATURE_POLICY_IDENTIFIER);
throw new BadFormedSignatureException(I18n.getResource(ConstantesXADES.LIBRERIAXADES_VALIDARFIRMA_ERROR125));
} else if (numSignaturePolicyIdentifier == 1)
datosTipoFirma.setEsXAdES_EPES(true);
// Se comprueba que la firma sea XAdES-T buscando el nodo SignatureTimeStamp
NodeList nodosSignatureTimeStamp = firma.getElementsByTagNameNS(
esquemaXAdES, ConstantesXADES.SIGNATURE_TIME_STAMP);
int numSignatureTimeStamp = nodosSignatureTimeStamp.getLength();
if (numSignatureTimeStamp > 0)
datosTipoFirma.setTipoXAdES(EnumFormatoFirma.XAdES_T);
// Se comprueba que la firma sea XAdES-C buscando el nodo CompleteCertificateRefs y CompleteRevocationRefs
NodeList nodosCompleteCertificateRefs = firma.getElementsByTagNameNS(
esquemaXAdES, ConstantesXADES.COMPLETE_CERTIFICATE_REFS);
int numCompleteCertificateRefs = nodosCompleteCertificateRefs.getLength();
NodeList nodosCompleteRevocationRefs = firma.getElementsByTagNameNS(
esquemaXAdES, ConstantesXADES.COMPLETE_REVOCATION_REFS);
int numCompleteRevocationRefs = nodosCompleteRevocationRefs.getLength();
if (numCompleteCertificateRefs > 1 || numCompleteCertificateRefs != numCompleteRevocationRefs) {
if (numCompleteCertificateRefs > 1) {
// El nodo CompleteCertificateRefs no es único
log.error(I18n.getResource(ConstantesXADES.LIBRERIAXADES_FIRMAXML_ERROR_36) +
ConstantesXADES.ESPACIO + ConstantesXADES.COMPLETE_CERTIFICATE_REFS +
ConstantesXADES.ESPACIO + I18n.getResource(ConstantesXADES.LIBRERIAXADES_FIRMAXML_ERROR_38) +
ConstantesXADES.ESPACIO + numCompleteCertificateRefs);
} else {
// El nodo CompleteRevocationRefs no es único
log.error(I18n.getResource(ConstantesXADES.LIBRERIAXADES_FIRMAXML_ERROR_36) +
ConstantesXADES.ESPACIO + ConstantesXADES.COMPLETE_REVOCATION_REFS +
ConstantesXADES.ESPACIO + I18n.getResource(ConstantesXADES.LIBRERIAXADES_FIRMAXML_ERROR_38) +
ConstantesXADES.ESPACIO + numCompleteRevocationRefs);
}
// Firma XAdES-C mal formada
throw new BadFormedSignatureException(I18n.getResource(ConstantesXADES.LIBRERIAXADES_VALIDARFIRMA_ERROR126));
} else if (numCompleteCertificateRefs == 1 && numCompleteCertificateRefs == numCompleteRevocationRefs) {
if (ConstantesXADES.SCHEMA_XADES_111.equals(esquemaXAdES) && !EnumFormatoFirma.XAdES_T.equals(datosTipoFirma.getTipoXAdES())) {
// La firma es una XAdES-C mal formada porque carece del nivel XAdES-T en el esquema 1.1.1
log.error(I18n.getResource(ConstantesXADES.LIBRERIAXADES_VALIDARFIRMA_ERROR130));
throw new BadFormedSignatureException(I18n.getResource(ConstantesXADES.LIBRERIAXADES_VALIDARFIRMA_ERROR126));
} else {
esXAdES_C = true;
datosTipoFirma.setTipoXAdES(EnumFormatoFirma.XAdES_C);
}
}
// Se comprueba que la firma sea XAdES-A buscando los nodos ArchiveTimeStamp, CertificateValues y RevocationValues
NodeList nodosArchiveTimeStamp = firma.getElementsByTagNameNS(
esquemaXAdES, ConstantesXADES.ARCHIVE_TIME_STAMP);
int numArchiveTimeStamp = nodosArchiveTimeStamp.getLength();
NodeList nodosCertificateValues = firma.getElementsByTagNameNS(
esquemaXAdES, ConstantesXADES.CERTIFICATE_VALUES);
int numCertificateValues = nodosCertificateValues.getLength();
NodeList nodosRevocationValues = firma.getElementsByTagNameNS(
esquemaXAdES, ConstantesXADES.REVOCATION_VALUES);
int numRevocationValues = nodosRevocationValues.getLength();
if (numArchiveTimeStamp > 0) {
if (numCertificateValues < 1 || numRevocationValues < 1) {
// La firma es una XAdES-A mal formada porque carece de los nodos Certificatevalues y/o RevocationValues
log.error(I18n.getResource(ConstantesXADES.LIBRERIAXADES_VALIDARFIRMA_ERROR131));
// Firma XAdES-A mal formada
throw new BadFormedSignatureException(I18n.getResource(ConstantesXADES.LIBRERIAXADES_VALIDARFIRMA_ERROR127));
} else if (numCertificateValues > 1 || numRevocationValues > 1) {
// La firma es una XAdES-A mal formada porque los nodos Certificatevalues y/o RevocationValues no son únicos
log.error(I18n.getResource(ConstantesXADES.LIBRERIAXADES_VALIDARFIRMA_ERROR132));
throw new BadFormedSignatureException(I18n.getResource(ConstantesXADES.LIBRERIAXADES_VALIDARFIRMA_ERROR127));
} else if (ConstantesXADES.SCHEMA_XADES_111.equals(esquemaXAdES) && !esXAdES_C) {
// La firma es una XAdES-A mal formada porque carece del nivel XAdES-C en el esquema 1.1.1
log.error(I18n.getResource(ConstantesXADES.LIBRERIAXADES_VALIDARFIRMA_ERROR133));
throw new BadFormedSignatureException(I18n.getResource(ConstantesXADES.LIBRERIAXADES_VALIDARFIRMA_ERROR127));
} else
datosTipoFirma.setEsXAdES_A(true);
}
// Se comprueba que la firma sea XAdES-X buscando los nodos SigAndRefsTimeStamp y/o RefsOnlyTimeStamp
NodeList nodosSigAndRefTimeStamp = firma.getElementsByTagNameNS(
esquemaXAdES, ConstantesXADES.SIG_AND_REFS_TIME_STAMP);
int numSigAndRefsTimeStamp = nodosSigAndRefTimeStamp.getLength();
NodeList nodosRefsOnlyTimeStamp = firma.getElementsByTagNameNS(
esquemaXAdES, ConstantesXADES.REFS_ONLY_TIME_STAMP);
int numRefsOnlyTimeStamp = nodosRefsOnlyTimeStamp.getLength();
if (numSigAndRefsTimeStamp > 0 || numRefsOnlyTimeStamp > 0) {
if (!esXAdES_C) {
// La firma es una XAdES-X mal formada porque carece del nivel XAdES-C
log.error(I18n.getResource(ConstantesXADES.LIBRERIAXADES_VALIDARFIRMA_ERROR134));
// Firma XAdES-X mal formada
throw new BadFormedSignatureException(I18n.getResource(ConstantesXADES.LIBRERIAXADES_VALIDARFIRMA_ERROR128));
} else {
esXAdES_X = true;
datosTipoFirma.setTipoXAdES(EnumFormatoFirma.XAdES_X);
}
}
// Se comprueba que la firma sea XAdES-XL buscando los nodos CertificateValues y RevocationValues (ya buscados al validar XAdES-A)
if (numCertificateValues > 1 || numRevocationValues > 1) {
// La firma es una XAdES-A mal formada porque los nodos Certificatevalues y/o RevocationValues no son únicos
log.error(I18n.getResource(ConstantesXADES.LIBRERIAXADES_VALIDARFIRMA_ERROR132));
throw new BadFormedSignatureException(I18n.getResource(ConstantesXADES.LIBRERIAXADES_VALIDARFIRMA_ERROR127));
} else if (numCertificateValues == 1 && numRevocationValues == 1) {
// Si es XAdES-X y tiene los Values --> es XAdES-XL
// Si no es XAdES-X, tiene los Values y es XAdES-A --> No es XL pero no salta excepción (es XAdES-A)
// Si no es XAdES-X ni XAdES-A pero sí tiene los Values --> Excepción! es una XL mal formada
if (esXAdES_X) {
datosTipoFirma.setTipoXAdES(EnumFormatoFirma.XAdES_XL);
} else if (!datosTipoFirma.esXAdES_A()) {
// La firma es una XAdES-XL mal formada porque carece del nivel XAdES-X y no es XAdES-A
log.error(I18n.getResource(ConstantesXADES.LIBRERIAXADES_VALIDARFIRMA_ERROR135));
// Firma XAdES-XL mal formada
throw new BadFormedSignatureException(I18n.getResource(ConstantesXADES.LIBRERIAXADES_VALIDARFIRMA_ERROR129));
}
}
log.debug(datosTipoFirma.getTipoXAdES());
return datosTipoFirma;
}
/**
* Saca del nodo SigningTime los datos de la fecha de firma.
* Si falla el parseo de la firma porque no tiene un formato correcto devuelve un nulo
* @return Date fechaFirma
*/
private Date obtenerFechaFirma() {
// Buscamos la fecha de la firma
NodeList nodesSignTimeValue = null;
Date fechaFirma = null;
nodesSignTimeValue = firmaAValidar.getElementsByTagNameNS(esquema, LIBRERIAXADES_SIGNINGTIME);
if(nodesSignTimeValue.getLength() != 0)
{
Element stElement = (Element)nodesSignTimeValue.item(0);
String fecha = stElement.getFirstChild().getNodeValue() ;
fechaFirma = UtilidadFechas.parseaFechaXML(fecha);
}
if (fechaFirma == null) {
log.warn(I18n.getResource(LIBRERIAXADES_VALIDARFIRMA_ERROR63));
}
return fechaFirma;
}
/**
* Obtiene los roles del nodo ClaimedRoles
* @return ArrayList<String> roles
*/
private ArrayList<String> obtenerRoles (EstructuraFirma estructuraFirma) {
// Buscamos el valor de los roles
ArrayList<String> roles = new ArrayList<String>();
ArrayList<Element> lst = UtilidadTratarNodo.obtenerNodos(estructuraFirma.signedSignatureProperties, null, new NombreNodo(estructuraFirma.esquema, SIGNER_ROLE));
Iterator<Element> it = lst.iterator();
while (it.hasNext()) {
NodeList nodesClaimedRoles = it.next().getElementsByTagNameNS(esquema, LIBRERIAXADES_CLAIMEDROLE);
int nodesClaimedRolesLength = nodesClaimedRoles.getLength();
for (int i=0; i<nodesClaimedRolesLength; i++)
{
Element stElement = (Element)nodesClaimedRoles.item(i);
roles.add(stElement.getTextContent());
}
}
return (roles.size() > 0) ? roles : null;
}
/**
* Saca del nodo QualifyingProperties el esquema.
* Devuelve nulo si no esta en la lista de esquemas que se pueden validar.
* @return String esquema
*/
private EstructuraFirma obtenerEsquema(Element firma)
{
String esquema = null;
for (Iterator<String> it=esquemasParaValidar.iterator(); it.hasNext( ); )
{
esquema = ((String)it.next()).trim();
// identifica el nodo QualifyingProperties
Element qualifyingElement = null;
ArrayList<Element> nodosObject = UtilidadTratarNodo.obtenerNodos(firma, null, new NombreNodo(ConstantesXADES.SCHEMA_DSIG, ConstantesXADES.OBJECT));
Iterator<Element> itObject = nodosObject.iterator();
while (itObject.hasNext()) {
ArrayList<Element> nodosQualifyingProperties = UtilidadTratarNodo.obtenerNodos(itObject.next(), null, new NombreNodo(esquema, ConstantesXADES.LIBRERIAXADES_QUALIFYING_PROPERTIES));
if (nodosQualifyingProperties.size() > 0) {
qualifyingElement = nodosQualifyingProperties.get(0);
break;
}
}
if (qualifyingElement != null) {
EstructuraFirma ef = new EstructuraFirma();
ef.firma = firma;
ArrayList<Element> signedProperties = UtilidadTratarNodo.obtenerNodos(qualifyingElement, null, new NombreNodo(esquema, ConstantesXADES.SIGNED_PROPERTIES));
if (signedProperties.size() != 1)
return null;
ArrayList<Element> signedSignatureProperties = UtilidadTratarNodo.obtenerNodos(signedProperties.get(0), null, new NombreNodo(esquema, ConstantesXADES.SIGNED_SIGNATURE_PROPERTIES));
if (signedSignatureProperties.size() != 1)
return null;
ef.signedSignatureProperties = signedSignatureProperties.get(0);
ArrayList<Element> unsignedProperties = UtilidadTratarNodo.obtenerNodos(qualifyingElement, null, new NombreNodo(esquema, ConstantesXADES.UNSIGNED_PROPERTIES));
if (unsignedProperties.size() != 1)
ef.unsignedSignatureProperties = null;
else {
ArrayList<Element> unsignedSignatureProperties = UtilidadTratarNodo.obtenerNodos(unsignedProperties.get(0), null, new NombreNodo(esquema, ConstantesXADES.UNSIGNED_SIGNATURE_PROPERTIES));
if (unsignedSignatureProperties.size() != 1)
ef.unsignedSignatureProperties = null;
else
ef.unsignedSignatureProperties = unsignedSignatureProperties.get(0);
}
ef.esquema = esquema;
return ef;
}
}
return null;
}
}