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

xades4j.verification.XadesVerifierImpl Maven / Gradle / Ivy

Go to download

The XAdES4j library is an high-level, configurable and extensible Java implementation of XML Advanced Electronic Signatures (XAdES 1.3.2 and 1.4.1). It enables producing, verifying and extending signatures in the main XAdES forms: XAdES-BES, XAdES-EPES, XAdES-T and XAdES-C. Also, extended forms are supported through the enrichment of an existing signature.

There is a newer version: 2.3.0
Show newest version
/*
 * XAdES4j - A Java library for generation and verification of XAdES signatures.
 * Copyright (C) 2010 Luis Goncalves.
 *
 * XAdES4j is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 3 of the License, or any later version.
 *
 * XAdES4j is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License along
 * with XAdES4j. If not, see .
 */
package xades4j.verification;

import javax.inject.Inject;
import java.io.InputStream;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Set;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.signature.Reference;
import org.apache.xml.security.signature.SignedInfo;
import org.apache.xml.security.signature.XMLSignature;
import org.apache.xml.security.signature.XMLSignatureException;
import org.apache.xml.security.utils.resolver.ResourceResolverSpi;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import xades4j.properties.QualifyingProperty;
import xades4j.properties.UnsignedSignatureProperty;
import xades4j.XAdES4jException;
import xades4j.XAdES4jXMLSigException;
import xades4j.properties.data.CertRef;
import xades4j.properties.data.PropertyDataObject;
import xades4j.properties.UnsignedProperties;
import xades4j.production.XadesSignatureFormatExtender;
import xades4j.properties.SignatureTimeStampProperty;
import xades4j.properties.data.SignatureTimeStampData;
import xades4j.properties.data.SigningCertificateData;
import xades4j.providers.CertificateValidationProvider;
import xades4j.providers.ValidationData;
import xades4j.providers.X500NameStyleProvider;
import xades4j.utils.CollectionUtils;
import xades4j.utils.ObjectUtils;
import xades4j.utils.PropertiesUtils;
import xades4j.utils.ResolverAnonymous;
import xades4j.verification.KeyInfoProcessor.KeyInfoRes;
import xades4j.verification.RawSignatureVerifier.RawSignatureVerifierContext;
import xades4j.verification.SignatureUtils.ReferencesRes;
import xades4j.xml.unmarshalling.QualifyingPropertiesUnmarshaller;
import xades4j.xml.unmarshalling.UnmarshalException;

/**
 *
 * @author Luís
 */
class XadesVerifierImpl implements XadesVerifier
{

    static
    {
        org.apache.xml.security.Init.init();
        initFormExtension();
    }
    /**/
    private final CertificateValidationProvider certificateValidator;
    private final QualifyingPropertiesVerifier qualifyingPropertiesVerifier;
    private final QualifyingPropertiesUnmarshaller qualifPropsUnmarshaller;
    private final Set rawSigVerifiers;
    private final Set customSigVerifiers;
    private final X500NameStyleProvider x500NameStyleProvider;
    private boolean secureValidation;

    @Inject
    protected XadesVerifierImpl(
            CertificateValidationProvider certificateValidator,
            QualifyingPropertiesVerifier qualifyingPropertiesVerifier,
            QualifyingPropertiesUnmarshaller qualifPropsUnmarshaller,
            Set rawSigVerifiers,
            Set customSigVerifiers,
            X500NameStyleProvider x500NameStyleProvider)
    {
        if (ObjectUtils.anyNull(
                certificateValidator, qualifyingPropertiesVerifier, qualifPropsUnmarshaller, rawSigVerifiers, customSigVerifiers))
        {
            throw new NullPointerException("One or more arguments are null");
        }

        this.certificateValidator = certificateValidator;
        this.qualifyingPropertiesVerifier = qualifyingPropertiesVerifier;
        this.qualifPropsUnmarshaller = qualifPropsUnmarshaller;
        this.rawSigVerifiers = rawSigVerifiers;
        this.customSigVerifiers = customSigVerifiers;
        this.x500NameStyleProvider = x500NameStyleProvider;
        this.secureValidation = false;
    }

    void setAcceptUnknownProperties(boolean accept)
    {
        this.qualifPropsUnmarshaller.setAcceptUnknownProperties(accept);
    }

    void setSecureValidation(boolean secureValidation)
    {
        this.secureValidation = secureValidation;
    }

    @Override
    public XAdESVerificationResult verify(Element signatureElem, SignatureSpecificVerificationOptions verificationOptions) throws XAdES4jException
    {
        if (null == signatureElem)
        {
            throw new NullPointerException("Signature node not specified");
        }

        if (null == verificationOptions)
        {
            verificationOptions = SignatureSpecificVerificationOptions.EMPTY;
        }

        /* Unmarshal the signature */

        XMLSignature signature;
        try
        {
            signature = new XMLSignature(signatureElem, verificationOptions.getBaseUri(), this.secureValidation);
        } catch (XMLSecurityException ex)
        {
            throw new UnmarshalException("Bad XML signature", ex);
        }

        String signatureId = signature.getId();
        if (null == signatureId)
        {
            throw new UnmarshalException("XML signature doesn't have an Id");
        }

        ReferencesRes referencesRes = SignatureUtils.processReferences(signature);

        /* Apply early verifiers */

        RawSignatureVerifierContext rawCtx = new RawSignatureVerifierContext(signature);
        for (RawSignatureVerifier rawSignatureVerifier : this.rawSigVerifiers)
        {
            rawSignatureVerifier.verify(rawCtx);
        }

        /* Get and check the QualifyingProperties element */

        Element qualifyingPropsElem = SignatureUtils.getQualifyingPropertiesElement(signature);
        SignatureUtils.checkSignedPropertiesIncorporation(qualifyingPropsElem, referencesRes.signedPropsReference);

        // Check the QualifyingProperties 'Target' attribute.
        Node targetAttr = qualifyingPropsElem.getAttributeNodeNS(null, QualifyingProperty.TARGET_ATTR);
        if (null == targetAttr)
        {
            targetAttr = qualifyingPropsElem.getAttributeNodeNS(QualifyingProperty.XADES_XMLNS, QualifyingProperty.TARGET_ATTR);
            if (null == targetAttr)
            {
                throw new QualifyingPropertiesIncorporationException("QualifyingProperties Target attribute not present");
            }
        }
        String targetValue = targetAttr.getNodeValue();
        if (null == targetValue
                || !targetValue.startsWith("#")
                || !targetValue.substring(1).equals(signatureId))
        {
            throw new QualifyingPropertiesIncorporationException("QualifyingProperties target doesn't match the signature's Id");
        }

        /* Unmarshal the qualifying properties */

        QualifPropsDataCollectorImpl propsDataCollector = new QualifPropsDataCollectorImpl();
        qualifPropsUnmarshaller.unmarshalProperties(qualifyingPropsElem, propsDataCollector);
        Collection qualifPropsData = propsDataCollector.getPropertiesData();

        /* Certification path */

        Date validationDate = getValidationDate(qualifPropsData, signature, verificationOptions);
        CertRef signingCertRefAttempt = tryGetSigningCertificateRef(qualifPropsData);
        KeyInfoRes keyInfoRes = KeyInfoProcessor.process(signature.getKeyInfo(), signingCertRefAttempt, this.x500NameStyleProvider);
        ValidationData certValidationRes = this.certificateValidator.validate(
                keyInfoRes.certSelector,
                validationDate,
                keyInfoRes.keyInfoCerts);
        if (null == certValidationRes || certValidationRes.getCerts().isEmpty())
        {
            throw new NullPointerException("Certificate validator returned null or empty data");
        }
        X509Certificate validationCert = certValidationRes.getCerts().get(0);

        if (verificationOptions.checkKeyUsage())
        {
            // Check key usage.
            // - KeyUsage[0] = digitalSignature
            // - KeyUsage[1] = nonRepudiation
            boolean[] keyUsage = validationCert.getKeyUsage();
            if (keyUsage != null && !keyUsage[0] && !keyUsage[1])
            {
                throw new SigningCertificateKeyUsageException();
            }
        }

        /* Signature verification */

        // Core XML-DSIG verification.
        doCoreVerification(signature, verificationOptions, validationCert);

        // Create the properties verification context.
        QualifyingPropertyVerificationContext qPropsCtx = new QualifyingPropertyVerificationContext(
                signature,
                new QualifyingPropertyVerificationContext.CertificationChainData(
                certValidationRes.getCerts(),
                certValidationRes.getCrls(),
                keyInfoRes.issuerSerial,
                this.x500NameStyleProvider),
                /**/
                new QualifyingPropertyVerificationContext.SignedObjectsData(
                referencesRes.dataObjsReferences,
                signature));

        // Verify the properties. Data structure verification is included.
        Collection props = this.qualifyingPropertiesVerifier.verifyProperties(qualifPropsData, qPropsCtx);

        XAdESVerificationResult res = new XAdESVerificationResult(
                XAdESFormChecker.checkForm(props),
                signature,
                certValidationRes,
                props,
                referencesRes.dataObjsReferences);

        // Apply the custom signature verifiers.
        for (CustomSignatureVerifier customVer : this.customSigVerifiers)
        {
            customVer.verify(res, qPropsCtx);
        }

        return res;
    }
    
    /*************************************************************************************/

    private CertRef tryGetSigningCertificateRef(Collection qualifPropsData){
        // If the SigningCertificate property contains a single reference, it likely represents the signer's certificate.
        // In that case, it can be used as a fallback to identify the signing certificate when processing KeyInfo.
        // This is a temporary solution.
        // - Properties should probably be verified in two stages (before and after cert path creation).
        List certData = CollectionUtils.filterByType(qualifPropsData, SigningCertificateData.class);
        if (certData.size() == 1)
        {
            Collection certRefs = certData.get(0).getCertRefs();
            if (certRefs.size() == 1)
            {
                return certRefs.iterator().next();
            }
        }

        return null;
    }

    private Date getValidationDate(
            Collection qualifPropsData,
            XMLSignature signature,
            SignatureSpecificVerificationOptions verificationOptions) throws XAdES4jException
    {
        List sigTsData = CollectionUtils.filterByType(qualifPropsData, SignatureTimeStampData.class);

        // If no signature time-stamp is present, use the current date.
        if (sigTsData.isEmpty())
        {
            return verificationOptions.getDefaultVerificationDate();
        }

        // TODO support multiple SignatureTimeStamps (section 7.3 last paragraph of Standard v.1.4.2)
        // This is a temporary solution.
        // - Properties should probably be verified in two stages (before and after cert path creation).
        // - Had to remove the custom structure verifier that checked if the SigningCertificate data was present.
        QualifyingPropertyVerificationContext ctx = new QualifyingPropertyVerificationContext(
                signature,
                new QualifyingPropertyVerificationContext.CertificationChainData(
                new ArrayList(0),
                new ArrayList(0),
                null,
                this.x500NameStyleProvider),
                /**/
                new QualifyingPropertyVerificationContext.SignedObjectsData(
                new ArrayList(0),
                signature));
        Collection props = this.qualifyingPropertiesVerifier.verifyProperties(sigTsData, ctx);
        QualifyingProperty sigTs = props.iterator().next().getProperty();

        return ((SignatureTimeStampProperty) sigTs).getTime();
    }

    private static void doCoreVerification(
            XMLSignature signature,
            SignatureSpecificVerificationOptions verificationOptions,
            X509Certificate validationCert) throws XAdES4jXMLSigException, InvalidSignatureException
    {
        List resolvers = verificationOptions.getResolvers();
        if(!CollectionUtils.nullOrEmpty(resolvers))
        {
            for (ResourceResolverSpi resolver : resolvers)
            {
                signature.addResourceResolver(resolver);
            }
        }

        signature.setFollowNestedManifests(verificationOptions.isFollowManifests());

        InputStream nullURIReferenceData = verificationOptions.getDataForAnonymousReference();
        if (nullURIReferenceData != null)
        {
            signature.addResourceResolver(new ResolverAnonymous(nullURIReferenceData));
        }

        try
        {
            if (signature.checkSignatureValue(validationCert))
            {
                return;
            }
        }
        catch (XMLSignatureException ex)
        {
            throw new XAdES4jXMLSigException("Error verifying the signature", ex);
        }

        try
        {
            /* Failure due to the signature value or references validation? */

            if (signature.getSignedInfo().verifyReferences())
            // References are OK; this is a problem on the signature value
            // itself.
            {
                throw new SignatureValueException(signature);
            } else
            {
                // References are NOT OK; get the first invalid Reference.
                SignedInfo si = signature.getSignedInfo();
                for (int i = 0; i < si.getLength(); i++)
                {
                    Reference r = si.item(i);
                    if (!r.verify())
                    {
                        throw new ReferenceValueException(signature, r);
                    }
                }
            }
        }
        catch (XMLSecurityException ex)
        {
            throw new XAdES4jXMLSigException("Error verifying the references", ex);
        }
    }

    /*************************************************************************************/
    private static interface FormExtensionPropsCollector
    {

        void addProps(Collection usp,
                XAdESVerificationResult res);
    }
    private static FormExtensionPropsCollector[][] formsExtensionTransitions;

    private static void initFormExtension()
    {
        XAdESForm[] forms = XAdESForm.values();
        formsExtensionTransitions = new FormExtensionPropsCollector[forms.length][forms.length];

        // BES/EPES -> T
        FormExtensionPropsCollector tPropsCol = new FormExtensionPropsCollector()
        {

            @Override
            public void addProps(
                    Collection usp,
                    XAdESVerificationResult res)
            {
                PropertiesUtils.addXadesTProperties(usp);
            }
        };
        formsExtensionTransitions[XAdESForm.BES.ordinal()][XAdESForm.T.ordinal()] = tPropsCol;
        formsExtensionTransitions[XAdESForm.EPES.ordinal()][XAdESForm.T.ordinal()] = tPropsCol;

        // BES/EPES -> C
        FormExtensionPropsCollector cAndTPropsCol = new FormExtensionPropsCollector()
        {

            @Override
            public void addProps(
                    Collection usp,
                    XAdESVerificationResult res)
            {
                PropertiesUtils.addXadesCProperties(usp, res.getValidationData());
                PropertiesUtils.addXadesTProperties(usp);
            }
        };
        formsExtensionTransitions[XAdESForm.BES.ordinal()][XAdESForm.C.ordinal()] = cAndTPropsCol;
        formsExtensionTransitions[XAdESForm.EPES.ordinal()][XAdESForm.C.ordinal()] = cAndTPropsCol;

        // T -> C
        FormExtensionPropsCollector cPropsCol = new FormExtensionPropsCollector()
        {

            @Override
            public void addProps(
                    Collection usp,
                    XAdESVerificationResult res)
            {
                PropertiesUtils.addXadesCProperties(usp, res.getValidationData());
            }
        };
        formsExtensionTransitions[XAdESForm.T.ordinal()][XAdESForm.C.ordinal()] = cPropsCol;

        // C -> X
        FormExtensionPropsCollector xPropsCol = new FormExtensionPropsCollector()
        {

            @Override
            public void addProps(
                    Collection usp,
                    XAdESVerificationResult res)
            {
                PropertiesUtils.addXadesXProperties(usp);
            }
        };
        formsExtensionTransitions[XAdESForm.C.ordinal()][XAdESForm.X.ordinal()] = xPropsCol;

        // C -> X-L
        FormExtensionPropsCollector xlAndXPropsCol = new FormExtensionPropsCollector()
        {

            @Override
            public void addProps(
                    Collection usp,
                    XAdESVerificationResult res)
            {
                PropertiesUtils.addXadesXLProperties(usp, res.getValidationData());
                PropertiesUtils.addXadesXProperties(usp);
            }
        };
        formsExtensionTransitions[XAdESForm.C.ordinal()][XAdESForm.X_L.ordinal()] = xlAndXPropsCol;
    }

    @Override
    public XAdESVerificationResult verify(
            Element signatureElem,
            SignatureSpecificVerificationOptions verificationOptions,
            XadesSignatureFormatExtender formatExtender,
            XAdESForm finalForm) throws XAdES4jException
    {
        if (null == finalForm || null == formatExtender)
        {
            throw new NullPointerException("'finalForm' and 'formatExtender' cannot be null");
        }

        // The transitions matrix won't allow this, but this way I avoid the
        // unnecessary processing.
        if (finalForm.before(XAdESForm.T) || finalForm.after(XAdESForm.X_L))
        {
            throw new IllegalArgumentException("Signature format can only be extended to XAdES-T, C, X or X-L");
        }

        XAdESVerificationResult res = this.verify(signatureElem, verificationOptions);
        XAdESForm actualForm = res.getSignatureForm();

        if (actualForm.before(finalForm))
        {
            // Valid form transitions:
            // * BES/EPES -> T
            // * BES/EPES -> C
            // * T -> C
            // * C -> X
            // * C -> X-L
            // * X -> X-L (not supported with the library defaults: X cannot be verified)
            // * X-L -> A (not supported with the library defaults: X-L cannot be verified)

            FormExtensionPropsCollector finalFormPropsColector = formsExtensionTransitions[actualForm.ordinal()][finalForm.ordinal()];

            if (null == finalFormPropsColector)
            {
                throw new InvalidFormExtensionException(actualForm, finalForm);
            }

            Collection usp = new ArrayList(3);
            finalFormPropsColector.addProps(usp, res);

            formatExtender.enrichSignature(res.getXmlSignature(), new UnsignedProperties(usp));
        }
        return res;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy