xades4j.production.DataObjectDescsProcessor.java Source code

Java tutorial

Introduction

Here is the source code for xades4j.production.DataObjectDescsProcessor.java

Source

/*
 * 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 <http://www.gnu.org/licenses/>.
 */
package xades4j.production;

import xades4j.algorithms.Algorithm;

import com.google.inject.Inject;

import java.util.Collection;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
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.transforms.TransformationException;
import org.apache.xml.security.transforms.Transforms;
import org.apache.xml.security.utils.resolver.ResourceResolver;
import org.apache.xml.security.utils.resolver.implementations.ResolverAnonymous;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

import xades4j.UnsupportedAlgorithmException;
import xades4j.properties.DataObjectDesc;
import xades4j.providers.AlgorithmsProviderEx;
import xades4j.utils.DOMHelper;
import xades4j.xml.marshalling.algorithms.AlgorithmsParametersMarshallingProvider;

/**
 * Helper class that processes a set of data object descriptions.
 * 
 * @author Lus
 */
class DataObjectDescsProcessor {
    private final AlgorithmsProviderEx algorithmsProvider;
    private final AlgorithmsParametersMarshallingProvider algorithmsParametersMarshaller;

    @Inject
    DataObjectDescsProcessor(AlgorithmsProviderEx algorithmsProvider,
            AlgorithmsParametersMarshallingProvider algorithmsParametersMarshaller) {
        this.algorithmsProvider = algorithmsProvider;
        this.algorithmsParametersMarshaller = algorithmsParametersMarshaller;
    }

    /**
     * Returns the reference mappings resulting from the data object descriptions.
     * The corresponding {@code Reference}s and {@code Object}s are added to the
     * signature.
        
     * @throws UnsupportedAlgorithmException
     */
    Map<DataObjectDesc, Reference> process(SignedDataObjects signedDataObjects, XMLSignature xmlSignature)
            throws UnsupportedAlgorithmException {
        for (ResourceResolver resolver : signedDataObjects.getResourceResolvers()) {
            xmlSignature.addResourceResolver(resolver);
        }

        Collection<DataObjectDesc> dataObjsDescs = signedDataObjects.getDataObjectsDescs();
        Map<DataObjectDesc, Reference> referenceMappings = new IdentityHashMap<DataObjectDesc, Reference>(
                dataObjsDescs.size());

        String refUri, refType;
        Transforms transforms;
        String digestMethodUri = this.algorithmsProvider.getDigestAlgorithmForDataObjsReferences();
        boolean hasNullURIReference = false;
        /**/
        try {
            for (DataObjectDesc dataObjDesc : dataObjsDescs) {
                transforms = processTransforms(dataObjDesc, xmlSignature.getDocument());

                if (dataObjDesc instanceof DataObjectReference) {
                    // If the data object info is a DataObjectReference, the Reference uri
                    // and type are the ones specified on the object.
                    DataObjectReference dataObjRef = (DataObjectReference) dataObjDesc;
                    refUri = dataObjRef.getUri();
                    refType = dataObjRef.getType();
                } else if (dataObjDesc instanceof EnvelopedXmlObject) {
                    // If the data object info is a EnvelopedXmlObject we need to create a
                    // XMLObject to embed it. The Reference uri will refer the new
                    // XMLObject's id.
                    EnvelopedXmlObject envXmlObj = (EnvelopedXmlObject) dataObjDesc;
                    refUri = String.format("%s-object%d", xmlSignature.getId(), xmlSignature.getObjectLength());
                    refType = Reference.OBJECT_URI;

                    ObjectContainer xmlObj = new ObjectContainer(xmlSignature.getDocument());
                    xmlObj.setId(refUri);
                    xmlObj.appendChild(envXmlObj.getContent());
                    xmlObj.setMimeType(envXmlObj.getMimeType());
                    xmlObj.setEncoding(envXmlObj.getEncoding());
                    xmlSignature.appendObject(xmlObj);

                    refUri = '#' + refUri;
                } else if (dataObjDesc instanceof AnonymousDataObjectReference) {
                    if (hasNullURIReference) {
                        // This shouldn't happen because SignedDataObjects does the validation.
                        throw new IllegalStateException("Multiple AnonymousDataObjectReference detected");
                    }
                    hasNullURIReference = true;

                    refUri = refType = null;
                    AnonymousDataObjectReference anonymousRef = (AnonymousDataObjectReference) dataObjDesc;
                    xmlSignature.addResourceResolver(new ResolverAnonymous(anonymousRef.getDataStream()));
                } else {
                    throw new ClassCastException(
                            "Unsupported SignedDataObjectDesc. Must be one of DataObjectReference, EnvelopedXmlObject and AnonymousDataObjectReference");
                }

                // Add the Reference. References need an ID because data object
                // properties may refer them.
                xmlSignature.addDocument(refUri, transforms, digestMethodUri,
                        String.format("%s-ref%d", xmlSignature.getId(), referenceMappings.size()), // id
                        refType);

                // SignedDataObjects doesn't allow repeated instances, so there's no
                // need to check for duplicate entries on the map.
                Reference newRef = null;
                Reference ref;
                for (int i = 0; i < xmlSignature.getSignedInfo().getLength() && newRef == null; i++) {
                    ref = xmlSignature.getSignedInfo().item(i);
                    if (StringUtils.equals(ref.getURI(), refUri)) {
                        newRef = ref;
                    }
                }

                referenceMappings.put(dataObjDesc, newRef);
            }

        } catch (XMLSignatureException ex) {
            // -> xmlSignature.appendObject(xmlObj): not thrown when signing.
            // -> xmlSignature.addDocument(...): appears to be thrown when the digest
            //      algorithm is not supported.
            throw new UnsupportedAlgorithmException("Digest algorithm not supported in the XML Signature provider",
                    digestMethodUri, ex);
        } catch (org.apache.xml.security.exceptions.XMLSecurityException ex) {
            // -> xmlSignature.getSignedInfo().item(...): shouldn't be thrown
            //      when signing.
            throw new IllegalStateException(ex);
        }

        return Collections.unmodifiableMap(referenceMappings);
    }

    private Transforms processTransforms(DataObjectDesc dataObjDesc, Document document)
            throws UnsupportedAlgorithmException {
        Collection<Algorithm> dObjTransfs = dataObjDesc.getTransforms();
        if (dObjTransfs.isEmpty()) {
            return null;
        }

        Transforms transforms = new Transforms(document);

        for (Algorithm dObjTransf : dObjTransfs) {
            try {
                List<Node> transfParams = this.algorithmsParametersMarshaller.marshalParameters(dObjTransf,
                        document);
                if (null == transfParams) {
                    transforms.addTransform(dObjTransf.getUri());
                } else {
                    transforms.addTransform(dObjTransf.getUri(), DOMHelper.nodeList(transfParams));
                }
            } catch (TransformationException ex) {
                throw new UnsupportedAlgorithmException("Unsupported transform on XML Signature provider",
                        dObjTransf.getUri(), ex);
            }
        }
        return transforms;
    }
}