/**
* 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.pades.signature;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.SignerInfoGeneratorBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import eu.europa.esig.dss.DSSDocument;
import eu.europa.esig.dss.DSSException;
import eu.europa.esig.dss.DSSUtils;
import eu.europa.esig.dss.InMemoryDocument;
import eu.europa.esig.dss.MimeType;
import eu.europa.esig.dss.SignatureAlgorithm;
import eu.europa.esig.dss.SignatureLevel;
import eu.europa.esig.dss.SignatureValue;
import eu.europa.esig.dss.SigningOperation;
import eu.europa.esig.dss.ToBeSigned;
import eu.europa.esig.dss.cades.CMSUtils;
import eu.europa.esig.dss.cades.signature.CAdESLevelBaselineT;
import eu.europa.esig.dss.cades.signature.CustomContentSigner;
import eu.europa.esig.dss.pades.PAdESSignatureParameters;
import eu.europa.esig.dss.pdf.PDFSignatureService;
import eu.europa.esig.dss.pdf.PdfObjFactory;
import eu.europa.esig.dss.signature.AbstractSignatureService;
import eu.europa.esig.dss.signature.SignatureExtension;
import eu.europa.esig.dss.utils.Utils;
import eu.europa.esig.dss.validation.CertificateVerifier;
/**
* PAdES implementation of the DocumentSignatureService
*/
public class PAdESService extends AbstractSignatureService<PAdESSignatureParameters> {
private static final Logger LOG = LoggerFactory.getLogger(PAdESService.class);
private final PadesCMSSignedDataBuilder padesCMSSignedDataBuilder;
/**
* This is the constructor to create an instance of the {@code PAdESService}. 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 PAdESService(CertificateVerifier certificateVerifier) {
super(certificateVerifier);
padesCMSSignedDataBuilder = new PadesCMSSignedDataBuilder(certificateVerifier);
LOG.debug("+ PAdESService created");
}
private SignatureExtension<PAdESSignatureParameters> getExtensionProfile(SignatureLevel signatureLevel) {
switch (signatureLevel) {
case PAdES_BASELINE_B:
return null;
case PAdES_BASELINE_T:
return new PAdESLevelBaselineT(tspSource);
case PAdES_BASELINE_LT:
return new PAdESLevelBaselineLT(tspSource, certificateVerifier);
case PAdES_BASELINE_LTA:
return new PAdESLevelBaselineLTA(tspSource, certificateVerifier);
default:
throw new IllegalArgumentException("Signature format '" + signatureLevel + "' not supported");
}
}
@Override
public ToBeSigned getDataToSign(final DSSDocument toSignDocument, final PAdESSignatureParameters parameters) throws DSSException {
assertSigningDateInCertificateValidityRange(parameters);
final SignatureAlgorithm signatureAlgorithm = parameters.getSignatureAlgorithm();
final CustomContentSigner customContentSigner = new CustomContentSigner(signatureAlgorithm.getJCEId());
final PDFSignatureService pdfSignatureService = PdfObjFactory.getInstance().newPAdESSignatureService();
final InputStream inputStream = toSignDocument.openStream();
final byte[] messageDigest = pdfSignatureService.digest(inputStream, parameters, parameters.getDigestAlgorithm());
Utils.closeQuietly(inputStream);
SignerInfoGeneratorBuilder signerInfoGeneratorBuilder = padesCMSSignedDataBuilder.getSignerInfoGeneratorBuilder(parameters, messageDigest);
final CMSSignedDataGenerator generator = padesCMSSignedDataBuilder.createCMSSignedDataGenerator(parameters, customContentSigner,
signerInfoGeneratorBuilder, null);
final CMSProcessableByteArray content = new CMSProcessableByteArray(messageDigest);
CMSUtils.generateDetachedCMSSignedData(generator, content);
final byte[] dataToSign = customContentSigner.getOutputStream().toByteArray();
return new ToBeSigned(dataToSign);
}
@Override
public DSSDocument signDocument(final DSSDocument toSignDocument, final PAdESSignatureParameters parameters, final SignatureValue signatureValue)
throws DSSException {
assertSigningDateInCertificateValidityRange(parameters);
final SignatureAlgorithm signatureAlgorithm = parameters.getSignatureAlgorithm();
final CustomContentSigner customContentSigner = new CustomContentSigner(signatureAlgorithm.getJCEId(), signatureValue.getValue());
final PDFSignatureService pdfSignatureService = PdfObjFactory.getInstance().newPAdESSignatureService();
InputStream inputStream = toSignDocument.openStream();
final byte[] messageDigest = pdfSignatureService.digest(inputStream, parameters, parameters.getDigestAlgorithm());
Utils.closeQuietly(inputStream);
final SignerInfoGeneratorBuilder signerInfoGeneratorBuilder = padesCMSSignedDataBuilder.getSignerInfoGeneratorBuilder(parameters, messageDigest);
final CMSSignedDataGenerator generator = padesCMSSignedDataBuilder.createCMSSignedDataGenerator(parameters, customContentSigner,
signerInfoGeneratorBuilder, null);
final CMSProcessableByteArray content = new CMSProcessableByteArray(messageDigest);
CMSSignedData data = CMSUtils.generateDetachedCMSSignedData(generator, content);
final SignatureLevel signatureLevel = parameters.getSignatureLevel();
if (signatureLevel != SignatureLevel.PAdES_BASELINE_B) {
// use an embedded timestamp
CAdESLevelBaselineT cadesLevelBaselineT = new CAdESLevelBaselineT(tspSource, false);
data = cadesLevelBaselineT.extendCMSSignatures(data, parameters);
}
final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
final byte[] encodedData = CMSUtils.getEncoded(data);
inputStream = toSignDocument.openStream();
pdfSignatureService.sign(inputStream, encodedData, byteArrayOutputStream, parameters, parameters.getDigestAlgorithm());
Utils.closeQuietly(inputStream);
DSSDocument signature = new InMemoryDocument(byteArrayOutputStream.toByteArray());
signature.setMimeType(MimeType.PDF);
final SignatureExtension<PAdESSignatureParameters> extension = getExtensionProfile(signatureLevel);
if ((signatureLevel != SignatureLevel.PAdES_BASELINE_B) && (signatureLevel != SignatureLevel.PAdES_BASELINE_T) && (extension != null)) {
signature = extension.extendSignatures(signature, parameters);
}
parameters.reinitDeterministicId();
signature.setName(DSSUtils.getFinalFileName(toSignDocument, SigningOperation.SIGN, parameters.getSignatureLevel()));
return signature;
}
@Override
public DSSDocument extendDocument(DSSDocument original, PAdESSignatureParameters parameters) throws DSSException {
final SignatureExtension<PAdESSignatureParameters> extension = getExtensionProfile(parameters.getSignatureLevel());
if (extension != null) {
DSSDocument extended = extension.extendSignatures(original, parameters);
extended.setName(DSSUtils.getFinalFileName(original, SigningOperation.EXTEND, parameters.getSignatureLevel()));
return extended;
}
return original;
}
}