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

xades4j.production.SignerBES 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.2.2
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.production;

import org.apache.xml.security.c14n.Canonicalizer;
import org.apache.xml.security.c14n.InvalidCanonicalizerException;
import org.apache.xml.security.transforms.TransformationException;
import org.apache.xml.security.transforms.Transforms;
import xades4j.properties.QualifyingProperties;
import xades4j.properties.DataObjectDesc;
import com.google.inject.Inject;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.signature.ObjectContainer;
import org.apache.xml.security.signature.Reference;
import org.apache.xml.security.signature.XMLSignature;
import org.apache.xml.security.signature.XMLSignatureException;
import org.apache.xml.security.utils.Constants;
import org.apache.xml.security.utils.ElementProxy;
import org.apache.xml.security.utils.XMLUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import xades4j.algorithms.Algorithm;
import xades4j.properties.QualifyingProperty;
import xades4j.properties.SignedSignatureProperty;
import xades4j.properties.SigningCertificateProperty;
import xades4j.properties.UnsignedSignatureProperty;
import xades4j.UnsupportedAlgorithmException;
import xades4j.XAdES4jException;
import xades4j.XAdES4jXMLSigException;
import xades4j.properties.data.SigAndDataObjsPropertiesData;
import xades4j.providers.AlgorithmsProviderEx;
import xades4j.providers.BasicSignatureOptionsProvider;
import xades4j.providers.DataObjectPropertiesProvider;
import xades4j.providers.KeyingDataProvider;
import xades4j.providers.SignaturePropertiesProvider;
import xades4j.providers.SigningCertChainException;
import xades4j.utils.CanonicalizerUtils;
import xades4j.utils.DOMHelper;
import xades4j.utils.ObjectUtils;
import xades4j.utils.StringUtils;
import xades4j.utils.TransformUtils;
import xades4j.xml.marshalling.SignedPropertiesMarshaller;
import xades4j.xml.marshalling.UnsignedPropertiesMarshaller;
import xades4j.xml.marshalling.algorithms.AlgorithmsParametersMarshallingProvider;

/**
 * Base logic for producing XAdES signatures (XAdES-BES).
 * @author Luís
 */
class SignerBES implements XadesSigner
{

    static
    {
        Init.initXMLSec();
    }
    /**/
    private final KeyingDataProvider keyingProvider;
    private final AlgorithmsProviderEx algorithmsProvider;
    private final SignedDataObjectsProcessor dataObjectDescsProcessor;
    private final PropertiesDataObjectsGenerator propsDataObjectsGenerator;
    private final SignedPropertiesMarshaller signedPropsMarshaller;
    private final UnsignedPropertiesMarshaller unsignedPropsMarshaller;
    private final AlgorithmsParametersMarshallingProvider algorithmsParametersMarshaller;
    /**/
    private final KeyInfoBuilder keyInfoBuilder;
    private final QualifyingPropertiesProcessor qualifPropsProcessor;

    @Inject
    protected SignerBES(
            KeyingDataProvider keyingProvider,
            AlgorithmsProviderEx algorithmsProvider,
            BasicSignatureOptionsProvider basicSignatureOptionsProvider,
            SignedDataObjectsProcessor dataObjectDescsProcessor,
            SignaturePropertiesProvider signaturePropsProvider,
            DataObjectPropertiesProvider dataObjPropsProvider,
            PropertiesDataObjectsGenerator propsDataObjectsGenerator,
            SignedPropertiesMarshaller signedPropsMarshaller,
            UnsignedPropertiesMarshaller unsignedPropsMarshaller,
            AlgorithmsParametersMarshallingProvider algorithmsParametersMarshaller)
    {
        if (ObjectUtils.anyNull(
                keyingProvider, algorithmsProvider,
                signaturePropsProvider, dataObjPropsProvider, propsDataObjectsGenerator,
                signedPropsMarshaller, unsignedPropsMarshaller, algorithmsParametersMarshaller))
        {
            throw new NullPointerException("One or more arguments are null");
        }

        this.keyingProvider = keyingProvider;
        this.algorithmsProvider = algorithmsProvider;
        this.propsDataObjectsGenerator = propsDataObjectsGenerator;
        this.signedPropsMarshaller = signedPropsMarshaller;
        this.unsignedPropsMarshaller = unsignedPropsMarshaller;
        this.algorithmsParametersMarshaller = algorithmsParametersMarshaller;

        this.dataObjectDescsProcessor = dataObjectDescsProcessor;
        this.keyInfoBuilder = new KeyInfoBuilder(basicSignatureOptionsProvider, algorithmsProvider, algorithmsParametersMarshaller);
        this.qualifPropsProcessor = new QualifyingPropertiesProcessor(signaturePropsProvider, dataObjPropsProvider);
    }

    @Override
    public final XadesSignatureResult sign(
            SignedDataObjects signedDataObjects,
            Node parent) throws XAdES4jException
    {
        return sign(signedDataObjects, parent, SignatureAppendingStrategies.AsLastChild);
    }

    @Override
    public final XadesSignatureResult sign(
            SignedDataObjects signedDataObjects,
            Node referenceNode,
            SignatureAppendingStrategy appendingStrategy) throws XAdES4jException
    {
        if (null == referenceNode)
        {
            throw new NullPointerException("Reference node node cannot be null");
        }
        if (null == signedDataObjects)
        {
            throw new NullPointerException("References cannot be null");
        }
        if (signedDataObjects.isEmpty())
        {
            throw new IllegalArgumentException("Data objects list is empty");
        }

        Document signatureDocument = DOMHelper.getOwnerDocument(referenceNode);

        // Generate unique identifiers for the Signature and the SignedProperties.
        String signatureId = String.format("xmldsig-%s", UUID.randomUUID());
        String signedPropsId = String.format("%s-signedprops", signatureId);

        // Signing certificate chain (may contain only the signing certificate).
        List signingCertificateChain = this.keyingProvider.getSigningCertificateChain();
        if (null == signingCertificateChain || signingCertificateChain.isEmpty())
        {
            throw new SigningCertChainException("Signing certificate not provided");
        }
        X509Certificate signingCertificate = signingCertificateChain.get(0);

        // The XMLSignature (ds:Signature).
        XMLSignature signature = createSignature(
                signatureDocument,
                signedDataObjects.getBaseUri(),
                signingCertificate.getPublicKey().getAlgorithm());

        signature.setId(signatureId);

        /* References */
        // Process the data object descriptions to get the References and mappings.
        // After this call all the signed data objects References and XMLObjects
        // are added to the signature.
        Map referenceMappings = this.dataObjectDescsProcessor.process(
                signedDataObjects,
                signature);
        
        /* ds:KeyInfo */
        this.keyInfoBuilder.buildKeyInfo(signingCertificate, signature);

        /* QualifyingProperties element */
        // Create the QualifyingProperties element
        Element qualifyingPropsElem = ElementProxy.createElementForFamily(
                signature.getDocument(),
                QualifyingProperty.XADES_XMLNS, QualifyingProperty.QUALIFYING_PROPS_TAG);
        qualifyingPropsElem.setAttributeNS(null, QualifyingProperty.TARGET_ATTR, '#' + signatureId);
        qualifyingPropsElem.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:xades141", QualifyingProperty.XADESV141_XMLNS);
        // ds:Object to contain QualifyingProperties
        ObjectContainer qPropsXmlObj = new ObjectContainer(signature.getDocument());
        qPropsXmlObj.appendChild(qualifyingPropsElem);
        try
        {
            signature.appendObject(qPropsXmlObj);
        } catch (XMLSignatureException ex)
        {
            // -> xmlSignature.appendObject(xmlObj): not thrown when signing.
            throw new IllegalStateException(ex);
        }

        /* Collect the properties */
        // Get the format specific signature properties.
        Collection fsssp = new ArrayList(2);
        Collection fsusp = new ArrayList(2);
        getFormatSpecificSignatureProperties(fsssp, fsusp, signingCertificateChain);
        // Gather all the signature and data objects properties.
        QualifyingProperties qualifProps = qualifPropsProcessor.getQualifyingProperties(
                signedDataObjects, fsssp, fsusp);

        try
        {
            // The signature needs to be appended to the document from now on because
            // property data generation may need to dereference same-document data
            // object references.
            appendingStrategy.append(signature.getElement(), referenceNode);

            /* Signed properties */
            // Create the context for signed properties data objects generation.
            PropertiesDataGenerationContext propsDataGenCtx = new PropertiesDataGenerationContext(
                    signedDataObjects.getDataObjectsDescs(),
                    referenceMappings,
                    signatureDocument);
            // Generate the signed properties data objects. The data objects structure
            // is verifier in the process.
            SigAndDataObjsPropertiesData signedPropsData = this.propsDataObjectsGenerator.generateSignedPropertiesData(
                    qualifProps.getSignedProperties(),
                    propsDataGenCtx);
            // Marshal the signed properties data to the QualifyingProperties node.
            this.signedPropsMarshaller.marshal(signedPropsData, qualifyingPropsElem);
            Element signedPropsElem = DOMHelper.getFirstChildElement(qualifyingPropsElem);
            DOMHelper.setIdAsXmlId(signedPropsElem, signedPropsId);

            // SignedProperties reference
            // XAdES 6.3.1: "In order to protect the properties with the signature,
            // a ds:Reference element MUST be added to the XMLDSIG signature (...)
            // composed in such a way that it uses the SignedProperties element (...)
            // as the input for computing its corresponding digest. Additionally,
            // (...) use the Type attribute of this particular ds:Reference element,
            // with its value set to: http://uri.etsi.org/01903#SignedProperties."

            String digestAlgUri = algorithmsProvider.getDigestAlgorithmForDataObjsReferences();
            if (StringUtils.isNullOrEmptyString(digestAlgUri))
            {
                throw new NullPointerException("Digest algorithm URI not provided");
            }
            
            // Use same canonicalization URI as specified in the ds:CanonicalizationMethod for Signature.
            Algorithm canonAlg = this.algorithmsProvider.getCanonicalizationAlgorithmForSignature();

            try
            {
                CanonicalizerUtils.checkC14NAlgorithm(canonAlg);
                Transforms transforms = TransformUtils.createTransforms(canonAlg, this.algorithmsParametersMarshaller, signatureDocument);

                signature.addDocument('#' + signedPropsId, transforms, digestAlgUri, null, QualifyingProperty.SIGNED_PROPS_TYPE_URI);
            } catch (XMLSignatureException ex)
            {
                // Seems to be thrown when the digest algorithm is not supported. In
                // this case, if it wasn't thrown when processing the data objects it
                // shouldn't be thrown now!
                throw new UnsupportedAlgorithmException(
                        "Digest algorithm not supported in the XML Signature provider",
                        digestAlgUri, ex);
            }

            // Apply the signature
            try
            {
                PrivateKey signingKey = keyingProvider.getSigningKey(signingCertificate);
                signature.sign(signingKey);
            }
            catch (XMLSignatureException ex)
            {
                throw new XAdES4jXMLSigException(ex.getMessage(), ex);
            }
            // Set the ds:SignatureValue id.
            Element sigValueElem = DOMHelper.getFirstDescendant(
                    signature.getElement(),
                    Constants.SignatureSpecNS, Constants._TAG_SIGNATUREVALUE);
            DOMHelper.setIdAsXmlId(sigValueElem, String.format("%s-sigvalue", signatureId));

            /* Marshal unsigned properties */
            // Generate the unsigned properties data objects. The data objects structure
            // is verifier in the process.
            propsDataGenCtx.setTargetXmlSignature(signature);
            SigAndDataObjsPropertiesData unsignedPropsData = this.propsDataObjectsGenerator.generateUnsignedPropertiesData(
                    qualifProps.getUnsignedProperties(),
                    propsDataGenCtx);
            // Marshal the unsigned properties to the final QualifyingProperties node.
            this.unsignedPropsMarshaller.marshal(unsignedPropsData, qualifyingPropsElem);
        }
        catch (XAdES4jException ex)
        {
            appendingStrategy.revert(signature.getElement(), referenceNode);
            throw ex;
        }

        return new XadesSignatureResult(signature, qualifProps);
    }

    private XMLSignature createSignature(Document signatureDocument, String baseUri, String signingKeyAlgorithm) throws XAdES4jXMLSigException, UnsupportedAlgorithmException
    {
        Algorithm signatureAlg = this.algorithmsProvider.getSignatureAlgorithm(signingKeyAlgorithm);
        if (null == signatureAlg)
        {
            throw new NullPointerException("Signature algorithm not provided");
        }
        Element signatureAlgElem = createElementForAlgorithm(signatureAlg, Constants._TAG_SIGNATUREMETHOD, signatureDocument);


        Algorithm canonAlg = this.algorithmsProvider.getCanonicalizationAlgorithmForSignature();
        if (null == canonAlg)
        {
            throw new NullPointerException("Canonicalization algorithm not provided");
        }
        Element canonAlgElem = createElementForAlgorithm(canonAlg, Constants._TAG_CANONICALIZATIONMETHOD, signatureDocument);

        try
        {
            return new XMLSignature(signatureDocument, baseUri, signatureAlgElem, canonAlgElem);
        } catch (XMLSecurityException ex)
        {
            // Following the code, doesn't seem to be thrown at all.
            throw new XAdES4jXMLSigException(ex.getMessage(), ex);
        }
    }

    private Element createElementForAlgorithm(Algorithm algorithm, String elementName, Document signatureDocument) throws UnsupportedAlgorithmException
    {
        Element algorithmElem = XMLUtils.createElementInSignatureSpace(signatureDocument, elementName);
        algorithmElem.setAttributeNS(null, Constants._ATT_ALGORITHM, algorithm.getUri());

        List algorithmParams = this.algorithmsParametersMarshaller.marshalParameters(algorithm, signatureDocument);
        if (algorithmParams != null)
        {
            for (Node p : algorithmParams)
            {
                algorithmElem.appendChild(p);
            }
        }
        return algorithmElem;
    }

    /**
     * Override in subclasses to collect the signature properties that are mandatory
     * in the corresponding format.
     */
    protected void getFormatSpecificSignatureProperties(
            Collection formatSpecificSignedSigProps,
            Collection formatSpecificUnsignedSigProps,
            List signingCertificateChain) throws XAdES4jException
    {
        SigningCertificateProperty scp = new SigningCertificateProperty(signingCertificateChain);
        formatSpecificSignedSigProps.add(scp);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy