All Downloads are FREE. Search and download functionalities are using the official Maven repository.

es.gob.afirma.cert.signvalidation.ValidateBinarySignature Maven / Gradle / Ivy

There is a newer version: 1.8.2
Show newest version
/* Copyright (C) 2011 [Gobierno de Espana]
 * This file is part of "Cliente @Firma".
 * "Cliente @Firma" is free software; you can redistribute it and/or modify it under the terms of:
 *   - the GNU General Public License as published by the Free Software Foundation;
 *     either version 2 of the License, or (at your option) any later version.
 *   - or The European Software License; either version 1.1 or (at your option) any later version.
 * You may contact the copyright holder at: [email protected]
 */

package es.gob.afirma.cert.signvalidation;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.util.Iterator;
import java.util.logging.Logger;

import org.spongycastle.cert.X509CertificateHolder;
import org.spongycastle.cms.CMSException;
import org.spongycastle.cms.CMSProcessableByteArray;
import org.spongycastle.cms.CMSSignedData;
import org.spongycastle.cms.CMSSignerDigestMismatchException;
import org.spongycastle.cms.DefaultCMSSignatureAlgorithmNameGenerator;
import org.spongycastle.cms.SignerInformation;
import org.spongycastle.cms.SignerInformationVerifier;
import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.spongycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.spongycastle.operator.OperatorCreationException;
import org.spongycastle.operator.bc.BcDigestCalculatorProvider;
import org.spongycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
import org.spongycastle.util.Store;

import es.gob.afirma.cert.signvalidation.SignValidity.SIGN_DETAIL_TYPE;
import es.gob.afirma.cert.signvalidation.SignValidity.VALIDITY_ERROR;
import es.gob.afirma.core.AOInvalidFormatException;
import es.gob.afirma.core.signers.AOSigner;
import es.gob.afirma.signers.cades.AOCAdESSigner;
import es.gob.afirma.signers.cms.AOCMSSigner;

/** Validador de firmas binarias.
 * @author Carlos Gamuci
 * @author Tomás García-Merás */
public final class ValidateBinarySignature implements SignValider{

	@Override
	public SignValidity validate(final byte[] sign) throws IOException {
		return validate(sign, null);
	}

    /** Valida una firma binaria (CMS/CAdES). Si se especifican los datos que se firmaron
     * se comprobará que efectivamente fueron estos, mientras que si no se indican
     * se extraerán de la propia firma. Si la firma no contiene los datos no se realizara
     * esta comprobación.
     * Se validan los certificados en local revisando las fechas de validez de los certificados.
     * @param sign Firma binaria
     * @param data Datos firmados o {@code null} si se desea comprobar contra los datos incrustados
     *             en la firma.
     * @return Validez de la firma.
     * @throws IOException Si ocurren problemas relacionados con la lectura de la firma o los datos. */
    public static SignValidity validate(final byte[] sign, final byte[] data) throws IOException {

    	if (sign == null) {
    		throw new IllegalArgumentException("La firma a validar no puede ser nula"); //$NON-NLS-1$
    	}

    	try {
			if (data == null && new AOCAdESSigner().getData(sign) == null) {
				Logger.getLogger("es.gob.afirma").info( //$NON-NLS-1$
					"Se ha pedido validar una firma explicita sin proporcionar los datos firmados" //$NON-NLS-1$
				);
				return new SignValidity(SIGN_DETAIL_TYPE.UNKNOWN, VALIDITY_ERROR.NO_DATA);
			}
		}
    	catch (final AOInvalidFormatException e1) {
    		Logger.getLogger("es.gob.afirma").info( //$NON-NLS-1$
				"Se ha pedido validar una firma como CAdES, pero no es CAdES: " + e1  //$NON-NLS-1$
			);
    		return new SignValidity(SIGN_DETAIL_TYPE.KO, VALIDITY_ERROR.NO_SIGN);
		}

    	AOSigner signer = new AOCAdESSigner();
    	if (!signer.isSign(sign)) {
    	    signer = new AOCMSSigner();
    	    if (!signer.isSign(sign)) {
    	        return new SignValidity(SIGN_DETAIL_TYPE.KO, null);
    	    }
    	}

    	try {
    		verifySignatures(sign, data != null ? data : new AOCAdESSigner().getData(sign));
	    }
    	catch (final CertificateExpiredException e) {
    		// Certificado caducado
    		return new SignValidity(SIGN_DETAIL_TYPE.KO, VALIDITY_ERROR.CERTIFICATE_EXPIRED);
        }
    	catch (final CertificateNotYetValidException e) {
    		// Certificado aun no valido
    		return new SignValidity(SIGN_DETAIL_TYPE.KO, VALIDITY_ERROR.CERTIFICATE_NOT_VALID_YET);
        }
    	catch (final CMSSignerDigestMismatchException e) {
    		// La firma no es una firma binaria valida
            return new SignValidity(SIGN_DETAIL_TYPE.KO, VALIDITY_ERROR.NO_MATCH_DATA);
    	}
    	catch (final Exception e) {
            // La firma no es una firma binaria valida
            return new SignValidity(SIGN_DETAIL_TYPE.KO, null);
        }

    	return new SignValidity(SIGN_DETAIL_TYPE.OK, null);
    }

    /** Verifica la valides de una firma. Si la firma es válida, no hace nada. Si no es
     * válida, lanza una excepción.
     * @param sign Firma que se desea validar.
     * @param data Datos para la comprobación.
     * @throws CMSException Cuando la firma no tenga una estructura válida.
     * @throws CertificateExpiredException Cuando el certificado estáa caducado.
     * @throws CertificateNotYetValidException Cuando el certificado aun no es válido.
     * @throws IOException Cuando no se puede crear un certificado desde la firma para validarlo
     * @throws OperatorCreationException Cuando no se puede crear el validado de contenido de firma*/
    private static void verifySignatures(final byte[] sign, final byte[] data) throws CMSException,
                                                                                      CertificateException,
                                                                                      IOException,
                                                                                      OperatorCreationException {

        final CMSSignedData s;
        if (data == null) {
        	s = new CMSSignedData(sign);
        }
        else {
        	s = new CMSSignedData(new CMSProcessableByteArray(data), sign);
        }
        final Store store = s.getCertificates();

        final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); //$NON-NLS-1$

        for (final Object si : s.getSignerInfos().getSigners()) {
        	final SignerInformation signer = (SignerInformation) si;

			final Iterator certIt = store.getMatches(new CertHolderBySignerIdSelector(signer.getSID())).iterator();
            final X509Certificate cert = (X509Certificate) certFactory.generateCertificate(
        		new ByteArrayInputStream(
    				certIt.next().getEncoded()
				)
    		);

            cert.checkValidity();

            if (!signer.verify(new SignerInformationVerifier(
            	new	DefaultCMSSignatureAlgorithmNameGenerator(),
            	new DefaultSignatureAlgorithmIdentifierFinder(),
        		new JcaContentVerifierProviderBuilder().setProvider(new BouncyCastleProvider()).build(cert),
        		new BcDigestCalculatorProvider()
    		))) {
            	throw new CMSException("Firma no valida"); //$NON-NLS-1$
            }

        }

    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy