es.gob.afirma.cert.signvalidation.ValidateBinarySignature.java Source code

Java tutorial

Introduction

Here is the source code for es.gob.afirma.cert.signvalidation.ValidateBinarySignature.java

Source

/* 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.
 * Date: 11/01/11
 * You may contact the copyright holder at: soporte.afirma5@mpt.es
 */

package es.gob.afirma.cert.signvalidation;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.cert.CRLException;
import java.security.cert.CertStoreException;
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.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignerDigestMismatchException;
import org.bouncycastle.cms.DefaultCMSSignatureAlgorithmNameGenerator;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationVerifier;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.bc.BcDigestCalculatorProvider;
import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
import org.bouncycastle.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 {

    private ValidateBinarySignature() {
        // No permitimos la instanciacion
    }

    /** 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.
     * @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 CertStoreException e) {
            // Error al recuperar los certificados o estos no son validos
            return new SignValidity(SIGN_DETAIL_TYPE.KO, VALIDITY_ERROR.CERTIFICATE_PROBLEM);
        } 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 NoSuchAlgorithmException e) {
            // Algoritmo no reconocido
            return new SignValidity(SIGN_DETAIL_TYPE.KO, VALIDITY_ERROR.ALGORITHM_NOT_SUPPORTED);
        } catch (final NoMatchDataException e) {
            // Los datos indicados no coinciden con los datos de firma
            return new SignValidity(SIGN_DETAIL_TYPE.KO, VALIDITY_ERROR.NO_MATCH_DATA);
        } catch (final CRLException e) {
            // Problema en la validacion de las CRLs de la firma
            return new SignValidity(SIGN_DETAIL_TYPE.KO, VALIDITY_ERROR.CRL_PROBLEM);
        } 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 CertStoreException Cuando se encuentra un error en los certificados de
     * firma o estos no pueden recuperarse.
     * @throws CertificateExpiredException Cuando el certificado estáa caducado.
     * @throws CertificateNotYetValidException Cuando el certificado aun no es válido.
     * @throws NoSuchAlgorithmException Cuando no se reconoce o soporta alguno de los
     * algoritmos utilizados en la firma.
     * @throws NoMatchDataException Cuando los datos introducidos no coinciden con los firmados.
     * @throws CRLException Cuando ocurre un error con las CRL de la firma.
     * @throws NoSuchProviderException Cuando no se encuentran los proveedores de seguridad necesarios para validar la firma
     * @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, CertStoreException, NoSuchAlgorithmException, NoMatchDataException, CRLException,
            NoSuchProviderException, CertificateException, IOException, OperatorCreationException {

        final CMSSignedData s;
        if (data == null) {
            s = new CMSSignedData(sign);
        } else {
            s = new CMSSignedData(new CMSProcessableByteArray(data), sign);
        }
        final Store<X509CertificateHolder> 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<X509CertificateHolder> certIt = store
                    .getMatches(new CertHolderBySignerIdSelector(signer.getSID())).iterator();
            final X509Certificate cert = (X509Certificate) certFactory
                    .generateCertificate(new ByteArrayInputStream(certIt.next().getEncoded()));

            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$
            }

        }

    }

}