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

eu.europa.esig.dss.cades.signature.CAdESService Maven / Gradle / Ivy

Go to download

DSS CAdES contains the code for the creation and validation of CAdES signatures.

There is a newer version: 6.0.d4j.2
Show newest version
/**
 * 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.cades.signature;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.SignerInfoGeneratorBuilder;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.operator.bc.BcDigestCalculatorProvider;
import org.bouncycastle.tsp.TSPException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import eu.europa.esig.dss.cades.CAdESSignatureParameters;
import eu.europa.esig.dss.cades.CMSUtils;
import eu.europa.esig.dss.cades.validation.PrecomputedDigestCalculatorProvider;
import eu.europa.esig.dss.enumerations.DigestAlgorithm;
import eu.europa.esig.dss.enumerations.SignatureAlgorithm;
import eu.europa.esig.dss.enumerations.SignatureLevel;
import eu.europa.esig.dss.enumerations.SignaturePackaging;
import eu.europa.esig.dss.enumerations.TimestampType;
import eu.europa.esig.dss.model.DSSDocument;
import eu.europa.esig.dss.model.DSSException;
import eu.europa.esig.dss.model.DigestDocument;
import eu.europa.esig.dss.model.InMemoryDocument;
import eu.europa.esig.dss.model.SignatureValue;
import eu.europa.esig.dss.model.TimestampBinary;
import eu.europa.esig.dss.model.ToBeSigned;
import eu.europa.esig.dss.signature.AbstractSignatureService;
import eu.europa.esig.dss.signature.SignatureExtension;
import eu.europa.esig.dss.signature.SigningOperation;
import eu.europa.esig.dss.spi.DSSASN1Utils;
import eu.europa.esig.dss.spi.DSSUtils;
import eu.europa.esig.dss.utils.Utils;
import eu.europa.esig.dss.validation.CertificateVerifier;
import eu.europa.esig.dss.validation.timestamp.TimestampToken;

/**
 * CAdES implementation of DocumentSignatureService
 */
public class CAdESService extends AbstractSignatureService {

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

	private final CMSSignedDataBuilder cmsSignedDataBuilder;

	/**
	 * This is the constructor to create an instance of the {@code CAdESService}. A certificate verifier must be
	 * provided.
	 *
	 * @param certificateVerifier
	 *            {@code CertificateVerifier} provides information on the sources to be used in the validation process
	 *            in the context of a signature.
	 */
	public CAdESService(final CertificateVerifier certificateVerifier) {
		super(certificateVerifier);
		cmsSignedDataBuilder = new CMSSignedDataBuilder(certificateVerifier);
		LOG.debug("+ CAdESService created");
	}

	@Override
	public TimestampToken getContentTimestamp(DSSDocument toSignDocument, CAdESSignatureParameters parameters) {
		if (tspSource == null) {
			throw new DSSException("A TSPSource is required !");
		}
		DigestAlgorithm digestAlgorithm = parameters.getContentTimestampParameters().getDigestAlgorithm();
		TimestampBinary timeStampResponse = tspSource.getTimeStampResponse(digestAlgorithm, Utils.fromBase64(toSignDocument.getDigest(digestAlgorithm)));
		try {
			return new TimestampToken(timeStampResponse.getBytes(), TimestampType.CONTENT_TIMESTAMP);
		} catch (TSPException | IOException | CMSException e) {
			throw new DSSException("Cannot create a content TimestampToken", e);
		}
	}

	@Override
	public ToBeSigned getDataToSign(final DSSDocument toSignDocument, final CAdESSignatureParameters parameters) throws DSSException {
		Objects.requireNonNull(toSignDocument, "toSignDocument cannot be null!");
		Objects.requireNonNull(parameters, "SignatureParameters cannot be null!");
		
		assertSigningDateInCertificateValidityRange(parameters);
		final SignaturePackaging packaging = parameters.getSignaturePackaging();
		assertSignaturePackaging(packaging);

		final SignatureAlgorithm signatureAlgorithm = parameters.getSignatureAlgorithm();
		final CustomContentSigner customContentSigner = new CustomContentSigner(signatureAlgorithm.getJCEId());
		final DigestCalculatorProvider dcp = getDigestCalculatorProvider(toSignDocument, parameters);

		final SignerInfoGeneratorBuilder signerInfoGeneratorBuilder = cmsSignedDataBuilder.getSignerInfoGeneratorBuilder(dcp, parameters, false);
		final CMSSignedData originalCmsSignedData = getCmsSignedData(toSignDocument, parameters);

		final CMSSignedDataGenerator cmsSignedDataGenerator = cmsSignedDataBuilder.createCMSSignedDataGenerator(parameters, customContentSigner,
				signerInfoGeneratorBuilder, originalCmsSignedData);

		final DSSDocument toSignData = getToSignData(toSignDocument, parameters, originalCmsSignedData);
		final CMSTypedData content = CMSUtils.getContentToBeSign(toSignData);
		final boolean encapsulate = !SignaturePackaging.DETACHED.equals(packaging);
		CMSUtils.generateCMSSignedData(cmsSignedDataGenerator, content, encapsulate);
		final byte[] bytes = customContentSigner.getOutputStream().toByteArray();
		return new ToBeSigned(bytes);
	}

	@Override
	public DSSDocument signDocument(final DSSDocument toSignDocument, final CAdESSignatureParameters parameters, SignatureValue signatureValue)
			throws DSSException {
		Objects.requireNonNull(toSignDocument, "toSignDocument cannot be null!");
		Objects.requireNonNull(parameters, "SignatureParameters cannot be null!");
		Objects.requireNonNull(signatureValue, "SignatureValue cannot be null!");

		assertSigningDateInCertificateValidityRange(parameters);
		final SignaturePackaging packaging = parameters.getSignaturePackaging();
		assertSignaturePackaging(packaging);

		final SignatureAlgorithm signatureAlgorithm = parameters.getSignatureAlgorithm();
		final CustomContentSigner customContentSigner = new CustomContentSigner(signatureAlgorithm.getJCEId(), signatureValue.getValue());
		final DigestCalculatorProvider dcp = getDigestCalculatorProvider(toSignDocument, parameters);
		final SignerInfoGeneratorBuilder signerInfoGeneratorBuilder = cmsSignedDataBuilder.getSignerInfoGeneratorBuilder(dcp, parameters, true);
		final CMSSignedData originalCmsSignedData = getCmsSignedData(toSignDocument, parameters);
		if ((originalCmsSignedData == null) && SignaturePackaging.DETACHED.equals(packaging) && Utils.isCollectionEmpty(parameters.getDetachedContents())) {
			parameters.setDetachedContents(Arrays.asList(toSignDocument));
		}

		final CMSSignedDataGenerator cmsSignedDataGenerator = cmsSignedDataBuilder.createCMSSignedDataGenerator(parameters, customContentSigner,
				signerInfoGeneratorBuilder, originalCmsSignedData);

		final DSSDocument toSignData = getToSignData(toSignDocument, parameters, originalCmsSignedData);
		final CMSTypedData content = CMSUtils.getContentToBeSign(toSignData);

		final boolean encapsulate = !SignaturePackaging.DETACHED.equals(packaging);
		final CMSSignedData cmsSignedData = CMSUtils.generateCMSSignedData(cmsSignedDataGenerator, content, encapsulate);
		DSSDocument signature = new CMSSignedDocument(cmsSignedData);

		final SignatureLevel signatureLevel = parameters.getSignatureLevel();
		if (!SignatureLevel.CAdES_BASELINE_B.equals(signatureLevel)) {
			// true: Only the last signature will be extended
			final SignatureExtension extension = getExtensionProfile(parameters, true);
			signature = extension.extendSignatures(signature, parameters);
		}
		signature.setName(getFinalFileName(toSignDocument, SigningOperation.SIGN, parameters.getSignatureLevel()));
		parameters.reinitDeterministicId();
		return signature;
	}

	private DigestCalculatorProvider getDigestCalculatorProvider(DSSDocument toSignDocument, CAdESSignatureParameters parameters) {
		DigestAlgorithm referenceDigestAlgorithm = parameters.getReferenceDigestAlgorithm();
		if (referenceDigestAlgorithm != null) {
			return new CustomMessageDigestCalculatorProvider(referenceDigestAlgorithm, toSignDocument.getDigest(referenceDigestAlgorithm));
		} else if (toSignDocument instanceof DigestDocument) {
			return new PrecomputedDigestCalculatorProvider((DigestDocument) toSignDocument);
		}
		return new BcDigestCalculatorProvider();
	}

	@Override
	public DSSDocument extendDocument(final DSSDocument toExtendDocument, final CAdESSignatureParameters parameters) {
		Objects.requireNonNull(toExtendDocument, "toExtendDocument is not defined!");
		Objects.requireNonNull(parameters, "Cannot extend the signature. SignatureParameters are not defined!");
		// false: All signature are extended
		final SignatureExtension extension = getExtensionProfile(parameters, false);
		final DSSDocument dssDocument = extension.extendSignatures(toExtendDocument, parameters);
		dssDocument.setName(getFinalFileName(toExtendDocument, SigningOperation.EXTEND, parameters.getSignatureLevel()));
		return dssDocument;
	}

	/**
	 * This method retrieves the data to be signed. It this data is located within a signature then it is extracted.
	 *
	 * @param toSignDocument
	 *            document to sign
	 * @param parameters
	 *            set of the driving signing parameters
	 * @param originalCmsSignedData
	 *            the signed data extracted from an existing signature or null
	 * @return
	 */
	private DSSDocument getToSignData(final DSSDocument toSignDocument, final CAdESSignatureParameters parameters, final CMSSignedData originalCmsSignedData) {
		final List detachedContents = parameters.getDetachedContents();
		if (Utils.isCollectionNotEmpty(detachedContents)) {
			// CAdES only can sign one document
			// (ASiC-S -> the document to sign /
			// ASiC-E -> ASiCManifest)
			return detachedContents.get(0);
		} else {
			if (originalCmsSignedData == null) {
				return toSignDocument;
			} else {
				return getSignedContent(originalCmsSignedData);
			}
		}
	}

	/**
	 * This method returns the signed content of CMSSignedData.
	 *
	 * @param cmsSignedData
	 *            the already signed {@code CMSSignedData}
	 * @return the original toSignDocument or null
	 */
	private DSSDocument getSignedContent(final CMSSignedData cmsSignedData) {
		final CMSTypedData signedContent = cmsSignedData.getSignedContent();
		if (signedContent == null) {
			throw new DSSException("Unknown SignedContent");
		}
		final byte[] documentBytes = (byte[]) signedContent.getContent();
		return new InMemoryDocument(documentBytes);
	}

	/**
	 * @param parameters
	 *            set of driving signing parameters
	 * @param onlyLastCMSSignature
	 *            indicates if only the last CSM signature should be extended
	 * @return {@code SignatureExtension} related to the predefine profile
	 */
	private SignatureExtension getExtensionProfile(final CAdESSignatureParameters parameters, final boolean onlyLastCMSSignature) {
		final SignatureLevel signatureLevel = parameters.getSignatureLevel();
		Objects.requireNonNull(signatureLevel, "SignatureLevel must be defined!");
		switch (signatureLevel) {
			case CAdES_BASELINE_T:
				return new CAdESLevelBaselineT(tspSource, onlyLastCMSSignature);
			case CAdES_BASELINE_LT:
				return new CAdESLevelBaselineLT(tspSource, certificateVerifier, onlyLastCMSSignature);
			case CAdES_BASELINE_LTA:
				return new CAdESLevelBaselineLTA(tspSource, certificateVerifier, onlyLastCMSSignature);
			default:
				throw new DSSException("Unsupported signature format : " + signatureLevel);
		}
	}

	/**
	 * In case of an enveloping signature if the signed content's content is null then the null is returned.
	 *
	 * @param dssDocument
	 *            {@code DSSDocument} containing the data to be signed or {@code CMSSignedData}
	 * @param parameters
	 *            set of driving signing parameters
	 * @return the {@code CMSSignedData} if the dssDocument is an CMS signed message. Null otherwise.
	 */
	private CMSSignedData getCmsSignedData(final DSSDocument dssDocument, final CAdESSignatureParameters parameters) {
		CMSSignedData cmsSignedData = null;
		if (!(dssDocument instanceof DigestDocument) && DSSASN1Utils.isASN1SequenceTag(DSSUtils.readFirstByte(dssDocument))) {
			try {
				cmsSignedData = new CMSSignedData(DSSUtils.toByteArray(dssDocument));
				if (SignaturePackaging.ENVELOPING == parameters.getSignaturePackaging() && cmsSignedData.getSignedContent().getContent() == null) {
					cmsSignedData = null;
				}
			} catch (Exception e) {
				// not a parallel signature
			}
		}
		return cmsSignedData;
	}

	/**
	 * @param packaging
	 *            {@code SignaturePackaging} to be checked
	 * @throws DSSException
	 *             if the packaging is not supported for this kind of signature
	 */
	private void assertSignaturePackaging(final SignaturePackaging packaging) throws DSSException {
		if ((packaging != SignaturePackaging.ENVELOPING) && (packaging != SignaturePackaging.DETACHED)) {
			throw new DSSException("Unsupported signature packaging: " + packaging);
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy