Java tutorial
/* * 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; } }