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

eu.europa.esig.dss.xades.signature.XAdESSignatureBuilder Maven / Gradle / Ivy

/**
 * DSS - Digital Signature Services
 * Copyright (C) 2015 European Commission, provided under the CEF programme
 * 
 * This file is part of the "DSS - Digital Signature Services" project.
 * 
 * This library 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 2.1 of the License, or (at your option) any later version.
 * 
 * This library 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 this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package eu.europa.esig.dss.xades.signature;

import eu.europa.esig.dss.enumerations.CommitmentType;
import eu.europa.esig.dss.enumerations.DigestAlgorithm;
import eu.europa.esig.dss.enumerations.EncryptionAlgorithm;
import eu.europa.esig.dss.enumerations.ObjectIdentifier;
import eu.europa.esig.dss.enumerations.ObjectIdentifierQualifier;
import eu.europa.esig.dss.enumerations.SignatureAlgorithm;
import eu.europa.esig.dss.enumerations.SignaturePackaging;
import eu.europa.esig.dss.enumerations.TimestampType;
import eu.europa.esig.dss.spi.exception.IllegalInputException;
import eu.europa.esig.dss.model.CommitmentQualifier;
import eu.europa.esig.dss.model.CommonCommitmentType;
import eu.europa.esig.dss.model.DSSDocument;
import eu.europa.esig.dss.model.DSSException;
import eu.europa.esig.dss.model.Policy;
import eu.europa.esig.dss.model.SignerLocation;
import eu.europa.esig.dss.model.SpDocSpecification;
import eu.europa.esig.dss.model.UserNotice;
import eu.europa.esig.dss.model.x509.CertificateToken;
import eu.europa.esig.dss.spi.DSSASN1Utils;
import eu.europa.esig.dss.spi.DSSUtils;
import eu.europa.esig.dss.spi.x509.BaselineBCertificateSelector;
import eu.europa.esig.dss.spi.x509.tsp.TimestampInclude;
import eu.europa.esig.dss.spi.x509.tsp.TimestampToken;
import eu.europa.esig.dss.utils.Utils;
import eu.europa.esig.dss.spi.validation.CertificateVerifier;
import eu.europa.esig.dss.xades.DSSObject;
import eu.europa.esig.dss.xades.DSSXMLUtils;
import eu.europa.esig.dss.xades.SignatureBuilder;
import eu.europa.esig.dss.xades.XAdESSignatureParameters;
import eu.europa.esig.dss.xades.dataobject.DSSDataObjectFormat;
import eu.europa.esig.dss.xades.dataobject.DataObjectFormatBuilder;
import eu.europa.esig.dss.xades.reference.DSSReference;
import eu.europa.esig.dss.xades.reference.ReferenceBuilder;
import eu.europa.esig.dss.xades.reference.ReferenceIdProvider;
import eu.europa.esig.dss.xades.reference.ReferenceProcessor;
import eu.europa.esig.dss.xades.reference.ReferenceVerifier;
import eu.europa.esig.dss.xades.validation.XAdESAttributeIdentifier;
import eu.europa.esig.dss.xml.common.definition.DSSElement;
import eu.europa.esig.dss.xml.utils.DomUtils;
import eu.europa.esig.dss.xml.utils.XMLCanonicalizer;
import eu.europa.esig.dss.xades.definition.xades132.XAdES132Attribute;
import eu.europa.esig.dss.xml.common.definition.xmldsig.XMLDSigAttribute;
import eu.europa.esig.dss.xml.common.definition.xmldsig.XMLDSigElement;
import eu.europa.esig.dss.xml.common.definition.xmldsig.XMLDSigPath;
import org.apache.xml.security.transforms.Transforms;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;

import javax.xml.datatype.XMLGregorianCalendar;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * This class implements all the necessary mechanisms to build each form of the XML signature.
 *
 */
public abstract class XAdESSignatureBuilder extends XAdESBuilder implements SignatureBuilder {

	private static final Logger LOG = LoggerFactory.getLogger(XAdESSignatureBuilder.class);

	/**
	 * Indicates if the signature was already built. (Two steps building)
	 */
	protected boolean built = false;

	/**
	 * This is the reference to the original document to sign
	 */
	protected DSSDocument document;

	/** The canonicalization method used for KeyInfo signing */
	protected String keyInfoCanonicalizationMethod;

	/** The canonicalization method used for SignedInfo signing */
	protected String signedInfoCanonicalizationMethod;

	/** The canonicalization method used for SignedProperties signing */
	protected String signedPropertiesCanonicalizationMethod;

	/** The deterministic Id used for elements creation */
	protected final String deterministicId;

	/** This variable represents the current DOM signature object. */
	protected Element signatureDom;

	/** Cached KeyInfo element */
	protected Element keyInfoDom;
	/** Cached SignedInfo element */
	protected Element signedInfoDom;
	/** Cached SignatureValue element */
	protected Element signatureValueDom;
	/** Cached QualifyingProperties element */
	protected Element qualifyingPropertiesDom;
	/** Cached SignedProperties element */
	protected Element signedPropertiesDom;
	/** Cached SignedSignatureProperties element */
	protected Element signedSignaturePropertiesDom;
	/** Cached SignedDataObjectProperties element */
	protected Element signedDataObjectPropertiesDom;
	/** Cached UnsignedSignatureProperties element */
	protected Element unsignedSignaturePropertiesDom;

	/** Id-prefix for KeyInfo element */
	protected static final String KEYINFO_PREFIX = "keyInfo-";
	/** Id-prefix for SignatureValue element */
	protected static final String VALUE_PREFIX = "value-";
	/** Id-prefix for Signature element */
	protected static final String XADES_PREFIX = "xades-";

	/**
	 * Creates the signature according to the packaging
	 *
	 * @param params
	 *            The set of parameters relating to the structure and process of the creation or extension of the
	 *            electronic signature.
	 * @param document
	 *            The original document to sign.
	 * @param certificateVerifier
	 *            the certificate verifier with its OCSPSource,...
	 * @return the signature builder linked to the packaging
	 */
	public static XAdESSignatureBuilder getSignatureBuilder(final XAdESSignatureParameters params, final DSSDocument document,
			final CertificateVerifier certificateVerifier) {
		Objects.requireNonNull(params.getSignaturePackaging(), "Cannot create a SignatureBuilder. SignaturePackaging is not defined!");
		
		switch (params.getSignaturePackaging()) {
			case ENVELOPED:
				return new EnvelopedSignatureBuilder(params, document, certificateVerifier);
			case ENVELOPING:
				return new EnvelopingSignatureBuilder(params, document, certificateVerifier);
			case DETACHED:
				return new DetachedSignatureBuilder(params, document, certificateVerifier);
			case INTERNALLY_DETACHED:
				return new InternallyDetachedSignatureBuilder(params, document, certificateVerifier);
			default:
				throw new DSSException("Unsupported packaging " + params.getSignaturePackaging());
		}
	}

	/**
	 * The default constructor for SignatureBuilder.
	 *
	 * @param params
	 *            The set of parameters relating to the structure and process of the creation or extension of the
	 *            electronic signature.
	 * @param document
	 *            The original document to sign.
	 * @param certificateVerifier
	 *            the certificate verifier with its OCSPSource,...
	 */
	protected XAdESSignatureBuilder(final XAdESSignatureParameters params, final DSSDocument document, final CertificateVerifier certificateVerifier) {
		super(certificateVerifier);
		
		this.params = params;
		this.document = document;
		this.deterministicId = params.getDeterministicId();
		
		setCanonicalizationMethods(params);
	}
	
	private void setCanonicalizationMethods(final XAdESSignatureParameters params) {
		this.keyInfoCanonicalizationMethod = params.getKeyInfoCanonicalizationMethod();
		this.signedInfoCanonicalizationMethod = params.getSignedInfoCanonicalizationMethod();
		this.signedPropertiesCanonicalizationMethod = params.getSignedPropertiesCanonicalizationMethod();
	}

	/**
	 * This is the main method which is called to build the XML signature
	 *
	 * @return A byte array is returned with XML that represents the canonicalized SignedInfo segment of signature.
	 *         This data are used to define the SignatureValue element.
	 * @throws DSSException
	 *             if an error occurred
	 */
	public byte[] build() throws DSSException {

		assertSignaturePossible();

		ensureConfigurationValidity();
		
		xadesPath = getCurrentXAdESPath();

		initRootDocumentDom();

		incorporateFiles();

		incorporateSignatureDom();

		incorporateSignedInfo();

		incorporateSignatureValue();

		incorporateKeyInfo();

		incorporateObjects();

		/**
		 * We create  segment only now, because we need first to define the SignedProperties segment to
		 * calculate the digest of references.
		 */
		if (Utils.isArrayEmpty(params.getSignedData())) {
			incorporateReferences();
			incorporateReferenceSignedProperties();
			incorporateReferenceKeyInfo();
		}

		// Preparation of SignedInfo
		byte[] canonicalizedSignedInfo = XMLCanonicalizer.createInstance(signedInfoCanonicalizationMethod).canonicalize(getNodeToCanonicalize(signedInfoDom));
		if (LOG.isTraceEnabled()) {
			LOG.trace("Canonicalized SignedInfo         --> {}", new String(canonicalizedSignedInfo));
			final byte[] digest = DSSUtils.digest(DigestAlgorithm.SHA256, canonicalizedSignedInfo);
			LOG.trace("Canonicalized SignedInfo SHA256  --> {}", Utils.toBase64(digest));
		}
		built = true;
		return canonicalizedSignedInfo;
	}
	
	private void assertSignaturePossible() {
		if (!DomUtils.isDOM(document)) {
			return;
		}

		initRootDocumentDom();

		final NodeList signatureNodeList = DSSXMLUtils.getAllSignaturesExceptCounterSignatures(documentDom);
		if (signatureNodeList == null || signatureNodeList.getLength() == 0) {
			return;
		}

		final Node parentSignatureNode = getParentNodeOfSignature();
		final Set parentNodes = getParentNodesChain(parentSignatureNode);

		for (int ii = 0; ii < signatureNodeList.getLength(); ii++) {
			final Node signatureNode = signatureNodeList.item(ii);
			NodeList referenceNodeList = DSSXMLUtils.getReferenceNodeList(signatureNode);
			if (referenceNodeList == null || referenceNodeList.getLength() == 0) {
				continue;
			}

			for (int jj = 0; jj < referenceNodeList.getLength(); jj++) {
				final Node referenceNode = referenceNodeList.item(jj);
				if (isSignatureCoveredNodeAffected(referenceNode, parentNodes)) {
					assertDoesNotContainEnvelopedTransform(referenceNode);
				}
			}
		}
	}

	private Set getParentNodesChain(Node node) {
		final Set nodesChain = new LinkedHashSet<>();
		nodesChain.add(node);
		for (Node parentNode = node.getParentNode(); parentNode != null; parentNode = parentNode.getParentNode()) {
			nodesChain.add(parentNode);
		}
		return nodesChain;
	}

	private boolean isSignatureCoveredNodeAffected(Node referenceNode, Set affectedNodes) {
		final String id = DSSXMLUtils.getAttribute(referenceNode, XMLDSigAttribute.URI.getAttributeName());
		if (id == null) {
			return false;
		} else if (Utils.isStringEmpty(id)) {
			// covers the whole file
			return true;
		} else {
			Node referencedNode = DomUtils.getElementById(documentDom, id);
			return affectedNodes.contains(referencedNode);
		}
	}

	private void assertDoesNotContainEnvelopedTransform(final Node referenceNode) {
		NodeList transformList = DomUtils.getNodeList(referenceNode, XMLDSigPath.TRANSFORMS_TRANSFORM_PATH);
		if (transformList != null && transformList.getLength() > 0) {
			for (int jj = 0; jj < transformList.getLength(); jj++) {
				final Element transformElement = (Element) transformList.item(jj);
				String transformAlgorithm = transformElement
						.getAttribute(XMLDSigAttribute.ALGORITHM.getAttributeName());
				if (Transforms.TRANSFORM_ENVELOPED_SIGNATURE.equals(transformAlgorithm)) {
					throw new IllegalInputException(String.format(
							"The parallel signature is not possible! The provided file contains a signature with an '%s' transform.",
							Transforms.TRANSFORM_ENVELOPED_SIGNATURE));
				}
			}
		}
	}

	private void ensureConfigurationValidity() {
		checkSignaturePackagingValidity();

		final List references = params.getReferences();
		if (Utils.isCollectionEmpty(references)) {
			final ReferenceBuilder referenceBuilder = initReferenceBuilder();
			final List defaultReferences = referenceBuilder.build();
			// The SignatureParameters object is updated with the default references
			// in order to ensure validity on next steps
			params.getContext().setReferences(defaultReferences);
		} else {
			final ReferenceVerifier referenceVerifier = new ReferenceVerifier(params);
			referenceVerifier.checkReferencesValidity();
		}
	}

	private ReferenceBuilder initReferenceBuilder() {
		List detachedContent = Utils.isCollectionNotEmpty(params.getDetachedContents()) ?
				params.getDetachedContents() : Arrays.asList(document);
		final ReferenceIdProvider referenceIdProvider = new ReferenceIdProvider();
		referenceIdProvider.setSignatureParameters(params);
		return new ReferenceBuilder(detachedContent, params, referenceIdProvider);
	}
	
	private void checkSignaturePackagingValidity() {
		if (!SignaturePackaging.ENVELOPING.equals(params.getSignaturePackaging())) {
			if (params.isManifestSignature()) {
				throw new IllegalArgumentException(String.format("The signature packaging %s is not compatible with manifestSignature(true) configuration!",
						params.getSignaturePackaging()));
			}
			if (params.isEmbedXML()) {
				throw new IllegalArgumentException(String.format("The signature packaging %s is not compatible with embedXML(true) configuration!",
						params.getSignaturePackaging()));
			}
		}
	}

	/**
	 * This method is used to incorporate the provided documents within the final file
	 */
	protected void incorporateFiles() {
		// not implemented by default
	}

	/**
	 * This method is used to instantiate a root {@code Document} DOM, when needed
	 */
	protected void initRootDocumentDom() {
		if (documentDom == null) {
			documentDom = buildRootDocumentDom();
		}
	}

	/**
	 * Builds an empty {@code Document}
	 *
	 * @return {@link Document}
	 */
	protected Document buildRootDocumentDom() {
		return DomUtils.buildDOM();
	}

	/**
	 * This method creates a new instance of Signature element.
	 */
	public void incorporateSignatureDom() {
		signatureDom = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.SIGNATURE);
		DomUtils.addNamespaceAttribute(signatureDom, getXmldsigNamespace());
		signatureDom.setAttribute(XMLDSigAttribute.ID.getAttributeName(), deterministicId);

		final Node parentNodeOfSignature = getParentNodeOfSignature();
		incorporateSignatureDom(parentNodeOfSignature);
	}

	/**
	 * Returns a parent node of the signature
	 *
	 * @return {@link Node}
	 */
	protected Node getParentNodeOfSignature() {
		return documentDom;
	}

	/**
	 * Incorporates the signature element to the parent node
	 *
	 * @param parentNodeOfSignature {@link Node} the parent node
	 */
	protected void incorporateSignatureDom(Node parentNodeOfSignature) {
		parentNodeOfSignature.appendChild(signatureDom);
	}

	/**
	 * This method incorporates the SignedInfo tag
	 *
	 * 
	 *  {@code
	 *   	
	 * 			
	 *   		
	 *   		...
	 *   	
	 *  }
	 * 
*/ public void incorporateSignedInfo() { if (Utils.isArrayNotEmpty(params.getSignedData())) { LOG.debug("Using explicit SignedInfo from parameter"); signedInfoDom = DomUtils.buildDOM(params.getSignedData()).getDocumentElement(); signedInfoDom = (Element) documentDom.importNode(signedInfoDom, true); signatureDom.appendChild(signedInfoDom); return; } signedInfoDom = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.SIGNED_INFO); signatureDom.appendChild(signedInfoDom); incorporateCanonicalizationMethod(signedInfoDom, signedInfoCanonicalizationMethod); final Element signatureMethod = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.SIGNATURE_METHOD); signedInfoDom.appendChild(signatureMethod); final SignatureAlgorithm signatureAlgorithm = params.getSignatureAlgorithm(); final String signatureAlgorithmXMLId = signatureAlgorithm.getUri(); if (Utils.isStringBlank(signatureAlgorithmXMLId)) { throw new UnsupportedOperationException("Unsupported signature algorithm " + signatureAlgorithm); } signatureMethod.setAttribute(XMLDSigAttribute.ALGORITHM.getAttributeName(), signatureAlgorithmXMLId); } /** * This method created the CanonicalizationMethod tag like : * *
	 * 	{@code
	 * 		
	 * 	}
	 * 
* * @param parentDom * the parent element * @param signedInfoCanonicalizationMethod * the canonicalization algorithm */ private void incorporateCanonicalizationMethod(final Element parentDom, final String signedInfoCanonicalizationMethod) { final Element canonicalizationMethodDom = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.CANONICALIZATION_METHOD); parentDom.appendChild(canonicalizationMethodDom); canonicalizationMethodDom.setAttribute(XMLDSigAttribute.ALGORITHM.getAttributeName(), signedInfoCanonicalizationMethod); } /** * This method creates the ds:Reference elements in the signature. */ private void incorporateReferences() { ReferenceProcessor referenceProcessor = new ReferenceProcessor(params); referenceProcessor.incorporateReferences(signedInfoDom, params.getReferences(), getXmldsigNamespace()); } /** * Creates KeyInfo tag. * NOTE: when trust anchor baseline profile policy is defined only the certificates previous to the trust anchor are * included. * *
	 * 	{@code
	 * 		
	 * 			
	 *  			
	 * 					MIIB....
	 * 				
	 * 				
	 * 					MIIB+...
	 * 				
	 * 			
	 * 		
	 * }
	 * 
* *
	 * 	{@code
	 * 		
	 * 			
	 *  			
	 * 					MIIB....
	 * 				
	 * 				
	 * 					MIIB+...
	 * 				
	 * 			
	 * 		
	 * }
	 * 
* * @throws DSSException * if an error occurred */ protected void incorporateKeyInfo() throws DSSException { if (params.getSigningCertificate() == null && params.isGenerateTBSWithoutCertificate()) { LOG.debug("Signing certificate not available and must be added to signature DOM later"); return; } // final Element keyInfoElement = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.KEY_INFO); signatureDom.appendChild(keyInfoElement); if (params.isSignKeyInfo()) { keyInfoElement.setAttribute(XMLDSigAttribute.ID.getAttributeName(), KEYINFO_PREFIX + deterministicId); } List certificates = new BaselineBCertificateSelector(params.getSigningCertificate(), params.getCertificateChain()) .setTrustedCertificateSource(certificateVerifier.getTrustedCertSources()) .setTrustAnchorBPPolicy(params.bLevel().isTrustAnchorBPPolicy()) .getCertificates(); if (params.isAddX509SubjectName()) { for (CertificateToken token : certificates) { // final Element x509DataDom = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.X509_DATA); keyInfoElement.appendChild(x509DataDom); addSubjectAndCertificate(x509DataDom, token); } } else { // final Element x509DataDom = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.X509_DATA); keyInfoElement.appendChild(x509DataDom); for (CertificateToken token : certificates) { addCertificate(x509DataDom, token); } } this.keyInfoDom = keyInfoElement; } /** * This method creates the X509SubjectName (optional) and X509Certificate (mandatory) tags * *
	 * {@code
	 * 	...
	 * 	...
	 * }
	 * 
* * @param x509DataDom * the parent X509Data tag * @param token * the certificate to add */ private void addSubjectAndCertificate(final Element x509DataDom, final CertificateToken token) { DomUtils.addTextElement(documentDom, x509DataDom, getXmldsigNamespace(), XMLDSigElement.X509_SUBJECT_NAME, token.getSubject().getRFC2253()); addCertificate(x509DataDom, token); } /** * This method creates the X509Certificate tag which is mandatory * *
	 * {@code
	 * 	...
	 * }
	 * 
* * @param x509DataDom * the parent X509Data tag * @param token * the certificate to add */ private void addCertificate(final Element x509DataDom, final CertificateToken token) { DomUtils.addTextElement(documentDom, x509DataDom, getXmldsigNamespace(), XMLDSigElement.X509_CERTIFICATE, Utils.toBase64(token.getEncoded())); } /** * This method incorporates the ds:Object tags * *
	 * 	{@code
	 * 		
	 * 			...
	 * 		
	 * 		
	 * 			...
	 * 		
	 * }
	 * 
* */ protected void incorporateObjects() { incorporateQualifyingProperties(); incorporateSignedObjects(); incorporateCustomObjects(); } /** * This method incorporates the ds:Object with xades:QualifyingProperties element * *
	 * 	{@code
	 * 		
	 * 			
	 * 				
	 * 					...
	 * 				
	 * 			
	 * 		
	 * }
	 * 
* */ protected void incorporateQualifyingProperties() { if (Utils.isArrayNotEmpty(params.getSignedAdESObject())) { LOG.debug("Incorporating signed XAdES Object from parameter"); if (DomUtils.isDOM(params.getSignedAdESObject())) { Node signedObjectDom = DomUtils.buildDOM(params.getSignedAdESObject()).getDocumentElement(); signedObjectDom = documentDom.importNode(signedObjectDom, true); signatureDom.appendChild(signedObjectDom); } else { throw new IllegalArgumentException("The signed AdES Object shall represent an XML!"); } return; } final Element objectDom = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.OBJECT); signatureDom.appendChild(objectDom); qualifyingPropertiesDom = DomUtils.addElement(documentDom, objectDom, getXadesNamespace(), getCurrentXAdESElements().getElementQualifyingProperties()); DomUtils.addNamespaceAttribute(qualifyingPropertiesDom, getXadesNamespace()); qualifyingPropertiesDom.setAttribute(TARGET, DomUtils.toElementReference(deterministicId)); incorporateSignedProperties(); } /** * Incorporates the list of signed ds:Object elements (used for Enveloping packaging) */ protected void incorporateSignedObjects() { // process only for enforced objects by default final List references = params.getReferences(); for (final DSSReference reference : references) { if (reference.getObject() != null) { incorporateObject(reference.getObject()); } } } /** * Incorporates a list of custom ds:Object elements within the ds:Signature element */ protected void incorporateCustomObjects() { if (Utils.isCollectionNotEmpty(params.getObjects())) { for (DSSObject object : params.getObjects()) { incorporateObject(object); } } } /** * Incorporates the given {@code object} within the ds:Signature * * @param object {@link DSSObject} to incorporate */ protected void incorporateObject(DSSObject object) { if (object.getContent() == null) { throw new IllegalArgumentException("The content shall be defined inside DSSObject element! " + "Incorporation is not possible."); } // incorporate ds:Object dom final Element objectDom = DomUtils.addElement(documentDom, signatureDom, getXmldsigNamespace(), XMLDSigElement.OBJECT); // incorporate content if (DomUtils.isDOM(object.getContent())) { Document contentDom = DomUtils.buildDOM(object.getContent()); DomUtils.adoptChildren(objectDom, contentDom); } else { Node objectContentDom = documentDom.createTextNode(new String(DSSUtils.toByteArray(object.getContent()))); objectDom.appendChild(objectContentDom); } // incorporate Id attribute if (Utils.isStringNotBlank(object.getId())) { objectDom.setAttribute(XMLDSigAttribute.ID.getAttributeName(), object.getId()); } // incorporate MimeType attribute if (Utils.isStringNotBlank(object.getMimeType())) { objectDom.setAttribute(XMLDSigAttribute.MIME_TYPE.getAttributeName(), object.getMimeType()); } // incorporate Encoding attribute if (Utils.isStringNotBlank(object.getEncodingAlgorithm())) { objectDom.setAttribute(XMLDSigAttribute.ENCODING.getAttributeName(), object.getEncodingAlgorithm()); } } /** * This method incorporates ds:References * *
	 * 	{@code
	 * 		
	 *			
	 *				
	 *			
	 *			
	 *			uijX/nvuu8g10ZVEklEnYatvFe8=
	 *		
	 * }
	 * 
*/ protected void incorporateReferenceSignedProperties() { final Element reference = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.REFERENCE); signedInfoDom.appendChild(reference); reference.setAttribute(XMLDSigAttribute.TYPE.getAttributeName(), xadesPath.getSignedPropertiesUri()); reference.setAttribute(XMLDSigAttribute.URI.getAttributeName(), DomUtils.toElementReference(XADES_PREFIX + deterministicId)); final Element transforms = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.TRANSFORMS); reference.appendChild(transforms); final Element transform = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.TRANSFORM); transforms.appendChild(transform); transform.setAttribute(XMLDSigAttribute.ALGORITHM.getAttributeName(), signedPropertiesCanonicalizationMethod); final DigestAlgorithm digestAlgorithm = DSSXMLUtils.getReferenceDigestAlgorithmOrDefault(params); DSSXMLUtils.incorporateDigestMethod(reference, digestAlgorithm, getXmldsigNamespace()); // This is a workaround to ensure the canonicalization is performed successfully when same namespace prefix is used within the element if (getXmldsigNamespace().getPrefix() != null && getXmldsigNamespace().getPrefix().equals(getXadesNamespace().getPrefix())) { signedPropertiesDom = DSSXMLUtils.ensureNamespacesDefined(documentDom, deterministicId, xadesPath.getSignedPropertiesPath()); } final byte[] canonicalizedBytes = XMLCanonicalizer.createInstance(signedPropertiesCanonicalizationMethod).canonicalize(getNodeToCanonicalize(signedPropertiesDom)); if (LOG.isTraceEnabled()) { LOG.trace("Canonicalization method --> {}", signedPropertiesCanonicalizationMethod); LOG.trace("Canonicalized REF_2 --> {}", new String(canonicalizedBytes)); } incorporateDigestValueOfReference(reference, digestAlgorithm, canonicalizedBytes); } /** * Method incorporates KeyInfo ds:References. * *
	 * 	{@code
	 * 		
	 *			
	 *				
	 *			
	 *			
	 *			uijX/nvuu2g10ZVEklEnYatvFe4=
	 *		
	 * }
	 * 
*/ protected void incorporateReferenceKeyInfo() { if (!params.isSignKeyInfo()) { return; } final Element reference = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.REFERENCE); signedInfoDom.appendChild(reference); reference.setAttribute(XMLDSigAttribute.URI.getAttributeName(), DomUtils.toElementReference(KEYINFO_PREFIX + deterministicId)); final Element transforms = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.TRANSFORMS); reference.appendChild(transforms); final Element transform = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.TRANSFORM); transforms.appendChild(transform); transform.setAttribute(XMLDSigAttribute.ALGORITHM.getAttributeName(), keyInfoCanonicalizationMethod); final DigestAlgorithm digestAlgorithm = DSSXMLUtils.getReferenceDigestAlgorithmOrDefault(params); DSSXMLUtils.incorporateDigestMethod(reference, digestAlgorithm, getXmldsigNamespace()); final byte[] canonicalizedBytes = XMLCanonicalizer.createInstance(keyInfoCanonicalizationMethod).canonicalize(getNodeToCanonicalize(keyInfoDom)); if (LOG.isTraceEnabled()) { LOG.trace("Canonicalization method --> {}", keyInfoCanonicalizationMethod); LOG.trace("Canonicalized REF_KeyInfo --> {}", new String(canonicalizedBytes)); } incorporateDigestValueOfReference(reference, digestAlgorithm, canonicalizedBytes); } /** * Creates the ds:DigestValue DOM object for the given {@code canonicalizedBytes} * * @param referenceDom - the parent element to append new DOM element to * @param digestAlgorithm - {@link DigestAlgorithm} to use * @param canonicalizedBytes - canonicalized byte array of the relevant reference DOM to hash */ private void incorporateDigestValueOfReference(final Element referenceDom, final DigestAlgorithm digestAlgorithm, final byte[] canonicalizedBytes) { final Element digestValueDom = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.DIGEST_VALUE); final String base64EncodedDigestBytes = Utils.toBase64(DSSUtils.digest(digestAlgorithm, canonicalizedBytes)); final Text textNode = documentDom.createTextNode(base64EncodedDigestBytes); digestValueDom.appendChild(textNode); referenceDom.appendChild(digestValueDom); } /** * This method incorporates the signature value. */ protected void incorporateSignatureValue() { signatureValueDom = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.SIGNATURE_VALUE); signatureDom.appendChild(signatureValueDom); signatureValueDom.setAttribute(XMLDSigAttribute.ID.getAttributeName(), VALUE_PREFIX + deterministicId); } /** * Creates the SignedProperties DOM object element. * *
	 * {@code
	 * 		
	 * }
	 * 
*/ protected void incorporateSignedProperties() { signedPropertiesDom = DomUtils.addElement(documentDom, qualifyingPropertiesDom, getXadesNamespace(), getCurrentXAdESElements().getElementSignedProperties()); signedPropertiesDom.setAttribute(XMLDSigAttribute.ID.getAttributeName(), XADES_PREFIX + deterministicId); incorporateSignedSignatureProperties(); incorporateSignedDataObjectProperties(); } /** * Creates the SignedSignatureProperties DOM object element. * *
	 * {@code
	 * 		
	 * 		...
	 * 		
	 * }
	 * 
* */ protected void incorporateSignedSignatureProperties() { signedSignaturePropertiesDom = DomUtils.addElement(documentDom, signedPropertiesDom, getXadesNamespace(), getCurrentXAdESElements().getElementSignedSignatureProperties()); incorporateSigningTime(); incorporateSigningCertificate(); incorporatePolicy(); incorporateSignatureProductionPlace(); incorporateSignerRole(); } /** * Creates SignaturePolicyIdentifier DOM object: * *
	 * {@code
	 * 	
	 * 	    
	 * 	        
	 * 	            urn:oid:1.3.6.1.4.1.10015.1000.3.2.1
	 * 	        
	 * 	        
	 * 	            
	 * 	            3Tl1oILSvOAWomdI9VeWV6IA/32eSXRUri9kPEz1IVs=
	 * 	        
	 * 	        
	 * 	            
	 * 	                http://spuri.test
	 * 	            
	 * 	        
	 * 	    
	 * 	
	 * }
	 * 
*/ private void incorporatePolicy() { final Policy signaturePolicy = params.bLevel().getSignaturePolicy(); if (signaturePolicy != null) { final Element signaturePolicyIdentifierDom = DomUtils.addElement(documentDom, signedSignaturePropertiesDom, getXadesNamespace(), getCurrentXAdESElements().getElementSignaturePolicyIdentifier()); final String signaturePolicyId = signaturePolicy.getId(); if (Utils.isStringEmpty(signaturePolicyId)) { // implicit DomUtils.addElement(documentDom, signaturePolicyIdentifierDom, getXadesNamespace(), getCurrentXAdESElements().getElementSignaturePolicyImplied()); } else { // explicit final Element signaturePolicyIdDom = DomUtils.addElement(documentDom, signaturePolicyIdentifierDom, getXadesNamespace(), getCurrentXAdESElements().getElementSignaturePolicyId()); final Element sigPolicyIdDom = DomUtils.addElement(documentDom, signaturePolicyIdDom, getXadesNamespace(), getCurrentXAdESElements().getElementSigPolicyId()); final Element identifierDom = DomUtils.addTextElement(documentDom, sigPolicyIdDom, getXadesNamespace(), getCurrentXAdESElements().getElementIdentifier(), signaturePolicyId); final ObjectIdentifierQualifier qualifier = signaturePolicy.getQualifier(); if (qualifier != null) { identifierDom.setAttribute(XAdES132Attribute.QUALIFIER.getAttributeName(), qualifier.getValue()); } final String description = signaturePolicy.getDescription(); if (Utils.isStringNotEmpty(description)) { DomUtils.addTextElement(documentDom, sigPolicyIdDom, getXadesNamespace(), getCurrentXAdESElements().getElementDescription(), description); } final String[] documentationReferences = signaturePolicy.getDocumentationReferences(); if (Utils.isArrayNotEmpty(documentationReferences)) { incorporateDocumentationReferences(sigPolicyIdDom, documentationReferences); } if (signaturePolicy instanceof XmlPolicyWithTransforms) { final XmlPolicyWithTransforms xmlPolicy = (XmlPolicyWithTransforms) signaturePolicy; DSSXMLUtils.incorporateTransforms(signaturePolicyIdDom, xmlPolicy.getTransforms(), getXmldsigNamespace()); } if (signaturePolicy.getDigestAlgorithm() != null && signaturePolicy.getDigestValue() != null) { final Element sigPolicyHashDom = DomUtils.addElement(documentDom, signaturePolicyIdDom, getXadesNamespace(), getCurrentXAdESElements().getElementSigPolicyHash()); final DigestAlgorithm digestAlgorithm = signaturePolicy.getDigestAlgorithm(); incorporateDigestMethod(sigPolicyHashDom, digestAlgorithm); final byte[] hashValue = signaturePolicy.getDigestValue(); final String base64EncodedHashValue = Utils.toBase64(hashValue); incorporateDigestValue(sigPolicyHashDom, base64EncodedHashValue); } if (signaturePolicy.isSPQualifierPresent()) { incorporateSigPolicyQualifiers(signaturePolicyIdDom, signaturePolicy); } } } } /** * Creates SigPolicyQualifiers DOM object: * *
	 * {@code
	 * 	
	 * 	    
	 * 	        http://signinghubbeta.cloudapp.net:7777/adss/policy/sample_sig_policy_document.txt
	 * 	    
	 * 	    
	 * 	        
	 * 	            This is a test policy
	 * 	        
	 * 	    
	 * 	
	 * }
	 * 
*/ private void incorporateSigPolicyQualifiers(Element signaturePolicyIdDom, Policy signaturePolicy) { final Element sigPolicyQualifiers = DomUtils.addElement(documentDom, signaturePolicyIdDom, getXadesNamespace(), getCurrentXAdESElements().getElementSigPolicyQualifiers()); final String spUri = signaturePolicy.getSpuri(); if (Utils.isStringNotEmpty(spUri)) { final Element sigPolicyQualifier = DomUtils.addElement(documentDom, sigPolicyQualifiers, getXadesNamespace(), getCurrentXAdESElements().getElementSigPolicyQualifier()); DomUtils.addTextElement(documentDom, sigPolicyQualifier, getXadesNamespace(), getCurrentXAdESElements().getElementSPURI(), spUri); } final UserNotice userNotice = signaturePolicy.getUserNotice(); if (userNotice != null && !userNotice.isEmpty()) { DSSUtils.assertSPUserNoticeConfigurationValid(userNotice); final Element sigPolicyQualifier = DomUtils.addElement(documentDom, sigPolicyQualifiers, getXadesNamespace(), getCurrentXAdESElements().getElementSigPolicyQualifier()); final Element spUserNotice = DomUtils.addElement(documentDom, sigPolicyQualifier, getXadesNamespace(), getCurrentXAdESElements().getElementSPUserNotice()); final String organization = userNotice.getOrganization(); final int[] noticeNumbers = userNotice.getNoticeNumbers(); if (Utils.isStringNotEmpty(organization) && noticeNumbers != null && noticeNumbers.length > 0) { final Element noticeRef = DomUtils.addElement(documentDom, spUserNotice, getXadesNamespace(), getCurrentXAdESElements().getElementNoticeRef()); DomUtils.addTextElement(documentDom, noticeRef, getXadesNamespace(), getCurrentXAdESElements().getElementOrganization(), organization); final Element noticeNumbersElement = DomUtils.addElement(documentDom, noticeRef, getXadesNamespace(), getCurrentXAdESElements().getElementNoticeNumbers()); for (int number : noticeNumbers) { DomUtils.addTextElement(documentDom, noticeNumbersElement, getXadesNamespace(), getCurrentXAdESElements().getElementint(), String.valueOf(number)); } } final String explicitText = userNotice.getExplicitText(); if (Utils.isStringNotEmpty(explicitText)) { DomUtils.addTextElement(documentDom, spUserNotice, getXadesNamespace(), getCurrentXAdESElements().getElementExplicitText(), explicitText); } } final SpDocSpecification spDocSpecification = signaturePolicy.getSpDocSpecification(); if (spDocSpecification != null && Utils.isStringNotEmpty(spDocSpecification.getId())) { final Element sigPolicyQualifier = DomUtils.addElement(documentDom, sigPolicyQualifiers, getXadesNamespace(), getCurrentXAdESElements().getElementSigPolicyQualifier()); incorporateSPDocSpecification(sigPolicyQualifier, spDocSpecification); } } /** * Creates SigningTime DOM object element like : * *
	 * 	{@code
	 * 		2013-11-23T11:22:52Z
	 * 	}
	 * 
*/ private void incorporateSigningTime() { final Date signingDate = params.bLevel().getSigningDate(); final XMLGregorianCalendar xmlGregorianCalendar = DomUtils.createXMLGregorianCalendar(signingDate); final String xmlSigningTime = xmlGregorianCalendar.toXMLFormat(); final Element signingTimeDom = DomUtils.createElementNS(documentDom, getXadesNamespace(), getCurrentXAdESElements().getElementSigningTime()); signedSignaturePropertiesDom.appendChild(signingTimeDom); final Text textNode = documentDom.createTextNode(xmlSigningTime); signingTimeDom.appendChild(textNode); } /** * Creates SigningCertificate(V2) building block DOM object: * *
	 * {@code
	 * 	
	 * 		
	 * 			
	 * 				
	 * 				fj8SJujSXU4fi342bdtiKVbglA0=
	 * 			
	 * 			
	 * 				CN=ICA A,O=DSS,C=AA
	 * 				4
	 *			
	 *		
	 * 	
	 * }
	 * 
*/ private void incorporateSigningCertificate() { if (params.getSigningCertificate() == null && params.isGenerateTBSWithoutCertificate()) { return; } final Set certificates = new HashSet<>(); certificates.add(params.getSigningCertificate()); if (params.isEn319132()) { incorporateSigningCertificateV2(certificates); } else { incorporateSigningCertificateV1(certificates); } } private void incorporateSigningCertificateV1(Set certificates) { Element signingCertificateDom = DomUtils.addElement(documentDom, signedSignaturePropertiesDom, getXadesNamespace(), getCurrentXAdESElements().getElementSigningCertificate()); DigestAlgorithm signingCertificateDigestMethod = params.getSigningCertificateDigestMethod(); for (final CertificateToken certificate : certificates) { incorporateCert(signingCertificateDom, certificate, signingCertificateDigestMethod); } } private void incorporateSigningCertificateV2(Set certificates) { Element signingCertificateDom = DomUtils.addElement(documentDom, signedSignaturePropertiesDom, getXadesNamespace(), getCurrentXAdESElements().getElementSigningCertificateV2()); DigestAlgorithm signingCertificateDigestMethod = params.getSigningCertificateDigestMethod(); for (final CertificateToken certificate : certificates) { incorporateCert(signingCertificateDom, certificate, signingCertificateDigestMethod); } } /** * This method incorporates the SignedDataObjectProperties DOM element like : * *
	 * 	{@code
	 * 		 ...
	 * 			
	 * 				...
	 *			
	 *          
	 *              ...
	 *          
	 *          
	 *              ...
	 *          
	 *          
	 *              ...
	 *          
	 *		
	 * 	}
	 * 
*/ private void incorporateSignedDataObjectProperties() { incorporateDataObjectFormat(); incorporateCommitmentTypeIndications(); incorporateContentTimestamps(); } private Element getSignedDataObjectPropertiesDom() { /* * 4.3.5 The SignedDataObjectProperties container * * A XAdES signature shall not incorporate an empty SignedDataObjectProperties element. */ if (signedDataObjectPropertiesDom == null) { signedDataObjectPropertiesDom = DomUtils.addElement(documentDom, signedPropertiesDom, getXadesNamespace(), getCurrentXAdESElements().getElementSignedDataObjectProperties()); } return signedDataObjectPropertiesDom; } /** * This method incorporates the SignedDataObjectProperties DOM element like : * *
	 * 	{@code
	 * 		
	 * 			text/plain
	 * 			...
	 *		
	 * 	}
	 * 
*/ private void incorporateDataObjectFormat() { List dataObjectFormats = params.getDataObjectFormatList(); if (dataObjectFormats == null) { dataObjectFormats = new DataObjectFormatBuilder().setReferences(params.getReferences()).build(); } for (final DSSDataObjectFormat dataObjectFormat : dataObjectFormats) { assertDataObjectFormatValid(dataObjectFormat); // create xades:DataObjectFormat final Element dataObjectFormatDom = DomUtils.addElement(documentDom, getSignedDataObjectPropertiesDom(), getXadesNamespace(), getCurrentXAdESElements().getElementDataObjectFormat()); // add xades:DataObjectFormat/xades:Description if (dataObjectFormat.getDescription() != null) { final Element descriptionDom = DomUtils.addElement(documentDom, dataObjectFormatDom, getXadesNamespace(), getCurrentXAdESElements().getElementDescription()); DomUtils.setTextNode(documentDom, descriptionDom, dataObjectFormat.getDescription()); } // add xades:DataObjectFormat/xades:ObjectIdentifier if (dataObjectFormat.getObjectIdentifier() != null) { final Element objectIdentifierDom = DomUtils.addElement(documentDom, dataObjectFormatDom, getXadesNamespace(), getCurrentXAdESElements().getElementObjectIdentifier()); incorporateObjectIdentifier(objectIdentifierDom, dataObjectFormat.getObjectIdentifier()); } // add xades:DataObjectFormat/xades:MimeType if (dataObjectFormat.getMimeType() != null) { final Element mimeTypeDom = DomUtils.addElement(documentDom, dataObjectFormatDom, getXadesNamespace(), getCurrentXAdESElements().getElementMimeType()); DomUtils.setTextNode(documentDom, mimeTypeDom, dataObjectFormat.getMimeType()); } // add xades:DataObjectFormat/xades:Encoding if (dataObjectFormat.getEncoding() != null) { final Element encodingDom = DomUtils.addElement(documentDom, dataObjectFormatDom, getXadesNamespace(), getCurrentXAdESElements().getElementEncoding()); DomUtils.setTextNode(documentDom, encodingDom, dataObjectFormat.getEncoding()); } // add xades:DataObjectFormat@ObjectReference if (dataObjectFormat.getObjectReference() != null) { dataObjectFormatDom.setAttribute(XAdES132Attribute.OBJECT_REFERENCE.getAttributeName(), dataObjectFormat.getObjectReference()); } } } /** * This method verifies if the DataObjectFormat is conformant to the XAdES specification * * @param dataObjectFormat {@link DSSDataObjectFormat} to check */ private void assertDataObjectFormatValid(DSSDataObjectFormat dataObjectFormat) { Objects.requireNonNull(dataObjectFormat, "DataObjectFormat cannot be null!"); if (dataObjectFormat.getDescription() == null && dataObjectFormat.getObjectIdentifier() == null && dataObjectFormat.getMimeType() == null) { throw new IllegalArgumentException("At least one of the Description, ObjectIdentifier or MimeType " + "shall be defined for a DataObjectFormat object!"); } if (dataObjectFormat.getObjectReference() == null) { throw new IllegalArgumentException("ObjectReference attribute of DataObjectFormat shall be present!"); } if (!DomUtils.isElementReference(dataObjectFormat.getObjectReference())) { throw new IllegalArgumentException("ObjectReference attribute of DataObjectFormat " + "shall define a reference to an element within signature (i.e. shall begin with '#')!"); } } /** * This method incorporate the content-timestamps within the signature being created. */ private void incorporateContentTimestamps() { final List contentTimestamps = params.getContentTimestamps(); if (contentTimestamps == null) { return; } for (final TimestampToken contentTimestamp : contentTimestamps) { final TimestampType timeStampType = contentTimestamp.getTimeStampType(); Element timestampDom; if (TimestampType.ALL_DATA_OBJECTS_TIMESTAMP.equals(timeStampType)) { timestampDom = DomUtils.addElement(documentDom, getSignedDataObjectPropertiesDom(), getXadesNamespace(), getCurrentXAdESElements().getElementAllDataObjectsTimeStamp()); } else if (TimestampType.INDIVIDUAL_DATA_OBJECTS_TIMESTAMP.equals(timeStampType)) { timestampDom = DomUtils.addElement(documentDom, getSignedDataObjectPropertiesDom(), getXadesNamespace(), getCurrentXAdESElements().getElementIndividualDataObjectsTimeStamp()); } else { throw new UnsupportedOperationException("Only types ALL_DATA_OBJECTS_TIMESTAMP and INDIVIDUAL_DATA_OBJECTS_TIMESTAMP are allowed"); } addContentTimestamp(timestampDom, contentTimestamp); } } /** * This method incorporates the signer claimed roleType into signed signature properties. */ private void incorporateSignerRole() { final List claimedSignerRoles = params.bLevel().getClaimedSignerRoles(); final List signedAssertions = params.bLevel().getSignedAssertions(); Element signerRoleDom = null; if (claimedSignerRoles != null) { if (params.isEn319132()) { signerRoleDom = DomUtils.addElement(documentDom, signedSignaturePropertiesDom, getXadesNamespace(), getCurrentXAdESElements().getElementSignerRoleV2()); } else { signerRoleDom = DomUtils.addElement(documentDom, signedSignaturePropertiesDom, getXadesNamespace(), getCurrentXAdESElements().getElementSignerRole()); } if (Utils.isCollectionNotEmpty(claimedSignerRoles)) { final Element claimedRolesDom = DomUtils.addElement(documentDom, signerRoleDom, getXadesNamespace(), getCurrentXAdESElements().getElementClaimedRoles()); addRoles(claimedSignerRoles, claimedRolesDom, getCurrentXAdESElements().getElementClaimedRole()); } } if (signedAssertions != null && params.isEn319132()) { if (signerRoleDom == null){ signerRoleDom = DomUtils.addElement(documentDom, signedSignaturePropertiesDom, getXadesNamespace(), getCurrentXAdESElements().getElementSignerRoleV2()); } if (Utils.isCollectionNotEmpty(signedAssertions)) { final Element signedAssertionsDom = DomUtils.addElement(documentDom, signerRoleDom, getXadesNamespace(), getCurrentXAdESElements().getElementSignedAssertions()); addAssertions(signedAssertions, signedAssertionsDom); } } } private void addRoles(final List signerRoles, final Element rolesDom, final DSSElement roleType) { for (final String signerRole : signerRoles) { final Element roleDom = DomUtils.addElement(documentDom, rolesDom, getXadesNamespace(), roleType); DomUtils.setTextNode(documentDom, roleDom, signerRole); } } private void incorporateSignatureProductionPlace() { final SignerLocation signatureProductionPlace = params.bLevel().getSignerLocation(); if (signatureProductionPlace != null && !signatureProductionPlace.isEmpty()) { final Element signatureProductionPlaceDom; if (params.isEn319132()) { signatureProductionPlaceDom = DomUtils.addElement(documentDom, signedSignaturePropertiesDom, getXadesNamespace(), getCurrentXAdESElements().getElementSignatureProductionPlaceV2()); } else { signatureProductionPlaceDom = DomUtils.addElement(documentDom, signedSignaturePropertiesDom, getXadesNamespace(), getCurrentXAdESElements().getElementSignatureProductionPlace()); } final String city = signatureProductionPlace.getLocality(); if (city != null) { DomUtils.addTextElement(documentDom, signatureProductionPlaceDom, getXadesNamespace(), getCurrentXAdESElements().getElementCity(), city); } if (params.isEn319132()) { final String streetAddress = signatureProductionPlace.getStreetAddress(); if (streetAddress != null) { DomUtils.addTextElement(documentDom, signatureProductionPlaceDom, getXadesNamespace(), getCurrentXAdESElements().getElementStreetAddress(), streetAddress); } } final String stateOrProvince = signatureProductionPlace.getStateOrProvince(); if (stateOrProvince != null) { DomUtils.addTextElement(documentDom, signatureProductionPlaceDom, getXadesNamespace(), getCurrentXAdESElements().getElementStateOrProvince(), stateOrProvince); } final String postalCode = signatureProductionPlace.getPostalCode(); if (postalCode != null) { DomUtils.addTextElement(documentDom, signatureProductionPlaceDom, getXadesNamespace(), getCurrentXAdESElements().getElementPostalCode(), postalCode); } final String country = signatureProductionPlace.getCountry(); if (country != null) { DomUtils.addTextElement(documentDom, signatureProductionPlaceDom, getXadesNamespace(), getCurrentXAdESElements().getElementCountryName(), country); } } } /** * Below follows the schema definition for this element. * * * * ... * ...... * ...... * ......... * ......... * ...... * ...... * ... * * * * ...... * ......... * ...... * commitmentTypeIndications = params.bLevel().getCommitmentTypeIndications(); if (Utils.isCollectionNotEmpty(commitmentTypeIndications)) { for (final CommitmentType commitmentTypeIndication : commitmentTypeIndications) { assertCommitmentTypeNotNull(commitmentTypeIndication); final Element commitmentTypeIndicationDom = DomUtils.addElement(documentDom, getSignedDataObjectPropertiesDom(), getXadesNamespace(), getCurrentXAdESElements().getElementCommitmentTypeIndication()); final Element commitmentTypeIdDom = DomUtils.addElement(documentDom, commitmentTypeIndicationDom, getXadesNamespace(), getCurrentXAdESElements().getElementCommitmentTypeId()); incorporateObjectIdentifier(commitmentTypeIdDom, commitmentTypeIndication); String[] signedDataObjects = null; CommitmentQualifier[] commitmentTypeQualifiers = null; if (commitmentTypeIndication instanceof CommonCommitmentType) { CommonCommitmentType commonCommitmentType = (CommonCommitmentType) commitmentTypeIndication; signedDataObjects = commonCommitmentType.getSignedDataObjects(); commitmentTypeQualifiers = commonCommitmentType.getCommitmentTypeQualifiers(); } if (Utils.isArrayNotEmpty(signedDataObjects)) { // add xades:ObjectReference for (String signedDataObjectUri : signedDataObjects) { if (Utils.isStringBlank(signedDataObjectUri)) { throw new IllegalArgumentException("SignedDataObject URI cannot be null!"); } signedDataObjectUri = DomUtils.toElementReference(signedDataObjectUri); DomUtils.addTextElement(documentDom, commitmentTypeIndicationDom, getXadesNamespace(), getCurrentXAdESElements().getElementObjectReference(), signedDataObjectUri); } } else { // add xades:AllSignedDataObjects DomUtils.addElement(documentDom, commitmentTypeIndicationDom, getXadesNamespace(), getCurrentXAdESElements().getElementAllSignedDataObjects()); } // add xades:CommitmentTypeQualifiers if (Utils.isArrayNotEmpty(commitmentTypeQualifiers)) { final Element commitmentTypeQualifiersElement = DomUtils.addElement(documentDom, commitmentTypeIndicationDom, getXadesNamespace(), getCurrentXAdESElements().getElementCommitmentTypeQualifiers()); for (CommitmentQualifier commitmentQualifier : commitmentTypeQualifiers) { Objects.requireNonNull(commitmentQualifier, "CommitmentTypeQualifier cannot be null!"); DSSDocument content = commitmentQualifier.getContent(); if (content == null) { throw new IllegalArgumentException("CommitmentTypeQualifier content cannot be null!"); } final Element commitmentTypeQualifierElement = DomUtils.addElement(documentDom, commitmentTypeQualifiersElement, getXadesNamespace(), getCurrentXAdESElements().getElementCommitmentTypeQualifier()); // incorporate content Node objectContentDom; if (DomUtils.isDOM(content)) { objectContentDom = DomUtils.buildDOM(content).getDocumentElement(); objectContentDom = documentDom.importNode(objectContentDom, true); } else { LOG.info("None XML encoded CommitmentTypeQualifier has been provided. Incorporate as text node."); objectContentDom = documentDom.createTextNode(new String(DSSUtils.toByteArray(content))); } commitmentTypeQualifierElement.appendChild(objectContentDom); } } } } } /** * This method verifies if the commitment type is not null and contains at least one of the mandatory elements: * URI or OID * * @param commitmentType {@link CommitmentType} to check */ private void assertCommitmentTypeNotNull(CommitmentType commitmentType) { Objects.requireNonNull(commitmentType, "CommitmentType cannot be null!"); if (commitmentType.getUri() == null && commitmentType.getOid() == null) { throw new IllegalArgumentException("The URI or OID must be defined for commitmentTypeIndication for XAdES creation!"); } } /** * This method creates the xades:ObjectIdentifierType DOM object. * *
	 * {@code
	 *     
	 *         
	 *             
	 *             
	 *             
	 *         
	 *     
	 * }
	 * 
* * @param parentDom * {@link Element} the parent element * @param objectIdentifier * {@link ObjectIdentifier} */ private void incorporateObjectIdentifier(final Element parentDom, ObjectIdentifier objectIdentifier) { // add xades:Identifier incorporateIdentifier(parentDom, objectIdentifier); // add xades:Description String description = objectIdentifier.getDescription(); if (description != null) { DomUtils.addTextElement(documentDom, parentDom, getXadesNamespace(), getCurrentXAdESElements().getElementDescription(), description); } // add xades:DocumentationReferences String[] documentationReferences = objectIdentifier.getDocumentationReferences(); if (Utils.isArrayNotEmpty(documentationReferences)) { incorporateDocumentationReferences(parentDom, documentationReferences); } } /** * This method creates the xades:ObjectIdentifierType DOM object. * *
	 * {@code
	 *     
	 *         
	 *             
	 *             
	 *             
	 *         
	 *     
	 * }
	 * 
* * @param parentDom * {@link Element} the parent element * @param objectIdentifier * {@link ObjectIdentifier} */ private void incorporateIdentifier(final Element parentDom, ObjectIdentifier objectIdentifier) { String uri = objectIdentifier.getUri(); String oid = objectIdentifier.getOid(); ObjectIdentifierQualifier qualifier = objectIdentifier.getQualifier(); if (Utils.isStringEmpty(uri)) { if (Utils.isStringEmpty(oid)) { throw new IllegalArgumentException("The URI or OID must be defined for XAdES IdentifierType element!"); } if (qualifier == null) { throw new IllegalArgumentException("When using OID as object identifier in XAdES, " + "a Qualifier shall be provided! See EN 319 132-1 for more details."); } switch (qualifier) { case OID_AS_URI: if (DSSUtils.isUrnOid(oid)) { throw new IllegalArgumentException(String.format( "Qualifier '%s' shall not be used for URN encoded OID! " + "See EN 319 132-1 for more details.", qualifier)); } break; case OID_AS_URN: if (!DSSUtils.isUrnOid(oid)) { oid = DSSUtils.toUrnOid(oid); } break; default: throw new UnsupportedOperationException( String.format("The Qualifier '%s' is not supported!", qualifier)); } uri = oid; } else { if (qualifier != null) { throw new IllegalArgumentException("When using URI as object identifier in XAdES, " + "a Qualifier shall not be present! See EN 319 132-1 for more details."); } } // add xades:Identifier final Element identifierDom = DomUtils.addTextElement(documentDom, parentDom, getXadesNamespace(), getCurrentXAdESElements().getElementIdentifier(), uri); // add xades:Identifier@Qualifier if (qualifier != null) { identifierDom.setAttribute(XAdES132Attribute.QUALIFIER.getAttributeName(), qualifier.getValue()); } } private void incorporateDocumentationReferences(Element parentElement, String[] documentationReferences) { final Element documentReferencesDom = DomUtils.addElement(documentDom, parentElement, getXadesNamespace(), getCurrentXAdESElements().getElementDocumentationReferences()); for (String ref : documentationReferences) { DomUtils.addTextElement(documentDom, documentReferencesDom, getXadesNamespace(), getCurrentXAdESElements().getElementDocumentationReference(), ref); } } /** * Adds signature value to the signature and returns XML signature (InMemoryDocument) * * @param signatureValue byte array * @return {@link DSSDocument} representing the signature */ @Override public DSSDocument signDocument(final byte[] signatureValue) { if (!built) { build(); } final EncryptionAlgorithm encryptionAlgorithm = params.getEncryptionAlgorithm(); final byte[] signatureValueBytes = DSSASN1Utils.ensurePlainSignatureValue(encryptionAlgorithm, signatureValue); final String signatureValueBase64Encoded = Utils.toBase64(signatureValueBytes); final Text signatureValueNode = documentDom.createTextNode(signatureValueBase64Encoded); signatureValueDom.appendChild(signatureValueNode); return createXmlDocument(); } /** * Adds the content of a timestamp into a given timestamp element * * @param timestampElement {@link Element} * @param token {@link TimestampToken} */ protected void addContentTimestamp(final Element timestampElement, final TimestampToken token) { // List includes, String canonicalizationMethod, TimestampToken encapsulatedTimestamp) { // add includes: URI + referencedData = "true" // add canonicalizationMethod: Algorithm // add encapsulatedTimestamp: Encoding, Id, while its textContent is the base64 encoding of the data to digest final List includes = token.getTimestampIncludes(); if (includes != null) { for (final TimestampInclude include : includes) { final Element timestampIncludeElement = DomUtils.createElementNS(documentDom, getXadesNamespace(), getCurrentXAdESElements().getElementInclude()); String uri = DomUtils.toElementReference(include.getURI()); timestampIncludeElement.setAttribute(URI, uri); timestampIncludeElement.setAttribute(REFERENCED_DATA, "true"); timestampElement.appendChild(timestampIncludeElement); } } String canonicalizationMethod = token.getCanonicalizationMethod(); if (Utils.isStringNotEmpty(canonicalizationMethod)) { final Element canonicalizationMethodElement = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.CANONICALIZATION_METHOD); canonicalizationMethodElement.setAttribute(XMLDSigAttribute.ALGORITHM.getAttributeName(), canonicalizationMethod); timestampElement.appendChild(canonicalizationMethodElement); } else { throw new IllegalArgumentException("Unable to create a timestamp with empty canonicalization method. " + "See EN 319 132-1: 4.5 Managing canonicalization of XML nodesets."); } final Element encapsulatedTimestampElement = DomUtils.createElementNS(documentDom, getXadesNamespace(), getCurrentXAdESElements().getElementEncapsulatedTimeStamp()); encapsulatedTimestampElement.setTextContent(Utils.toBase64(token.getEncoded())); timestampElement.appendChild(encapsulatedTimestampElement); // Build Id after time-stamp incorporation to ensure {@code timestampElement} contains a new time-stamp final String timestampId = TIMESTAMP_PREFIX + toXmlIdentifier(XAdESAttributeIdentifier.build(timestampElement)); timestampElement.setAttribute(XMLDSigAttribute.ID.getAttributeName(), timestampId); encapsulatedTimestampElement.setAttribute(XMLDSigAttribute.ID.getAttributeName(), ENCAPSULATED_TIMESTAMP_PREFIX + timestampId); } /** * Returns a node to be canonicalized (applies indents if required) * * @param node {@link Node} * @return {@link Node} */ protected Node getNodeToCanonicalize(Node node) { if (params.isPrettyPrint()) { return DSSXMLUtils.getIndentedNode(documentDom, node); } return node; } @Override protected void alignNodes() { if (unsignedSignaturePropertiesDom != null) { DSSXMLUtils.alignChildrenIndents(unsignedSignaturePropertiesDom); } if (qualifyingPropertiesDom != null) { DSSXMLUtils.alignChildrenIndents(qualifyingPropertiesDom); } } private void addAssertions(final List signedAssertions, final Element rolesDom) { for (final String signedAssertion : signedAssertions) { final Element roleDom = DomUtils.addElement(documentDom, rolesDom, getXadesNamespace(), getCurrentXAdESElements().getElementSignedAssertion()); Document samlAssertion = DomUtils.buildDOM(signedAssertion); Element docEl = samlAssertion.getDocumentElement(); Node node = documentDom.importNode(docEl, true); roleDom.appendChild(node); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy