eu.europa.esig.dss.cades.signature.CAdESService Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dss-cades Show documentation
Show all versions of dss-cades Show documentation
DSS CAdES contains the code for the creation and validation of CAdES signatures.
/**
* 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);
}
}
}