/* * DSS - Digital Signature Services * * Copyright (C) 2013 European Commission, Directorate-General Internal Market and Services (DG MARKT), B-1049 Bruxelles/Brussel * * Developed by: 2013 ARHS Developments S.A. (rue Nicolas Bové 2B, L-1253 Luxembourg) http://www.arhs-developments.com * * This file is part of the "DSS - Digital Signature Services" project. * * "DSS - Digital Signature Services" 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. * * DSS 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 * "DSS - Digital Signature Services". If not, see <http://www.gnu.org/licenses/>. */ package eu.europa.ec.markt.dss.signature.pades; import java.io.ByteArrayOutputStream; import java.io.InputStream; import org.bouncycastle.cms.CMSException; 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.ec.markt.dss.DSSASN1Utils; import eu.europa.ec.markt.dss.DSSUtils; import eu.europa.ec.markt.dss.SignatureAlgorithm; import eu.europa.ec.markt.dss.exception.DSSException; import eu.europa.ec.markt.dss.exception.DSSNullException; import eu.europa.ec.markt.dss.parameter.SignatureParameters; import eu.europa.ec.markt.dss.signature.AbstractSignatureService; import eu.europa.ec.markt.dss.signature.DSSDocument; import eu.europa.ec.markt.dss.signature.InMemoryDocument; import eu.europa.ec.markt.dss.signature.MimeType; import eu.europa.ec.markt.dss.signature.SignatureExtension; import eu.europa.ec.markt.dss.signature.SignatureLevel; import eu.europa.ec.markt.dss.signature.cades.CAdESLevelBaselineT; import eu.europa.ec.markt.dss.signature.cades.CustomContentSigner; import eu.europa.ec.markt.dss.signature.pdf.PDFSignatureService; import eu.europa.ec.markt.dss.signature.pdf.PdfObjFactory; import eu.europa.ec.markt.dss.signature.token.SignatureTokenConnection; import eu.europa.ec.markt.dss.validation102853.CertificateVerifier; /** * PAdES implementation of the DocumentSignatureService * * @version $Revision$ - $Date$ */ public class PAdESService extends AbstractSignatureService { 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 getExtensionProfile(SignatureParameters parameters) { switch (parameters.getSignatureLevel()) { case PAdES_BASELINE_B: return null; case PAdES_BASELINE_T: return new PAdESLevelBaselineT(tspSource, certificateVerifier); case PAdES_BASELINE_LT: return new PAdESLevelBaselineLT(tspSource, certificateVerifier); case PAdES_BASELINE_LTA: return new PAdESLevelBaselineLTA(tspSource, certificateVerifier); default: throw new IllegalArgumentException("Signature format '" + parameters.getSignatureLevel() + "' not supported"); } } @Override public byte[] getDataToSign(final DSSDocument toSignDocument, final SignatureParameters 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()); DSSUtils.closeQuietly(inputStream); SignerInfoGeneratorBuilder signerInfoGeneratorBuilder = padesCMSSignedDataBuilder.getSignerInfoGeneratorBuilder(parameters, messageDigest); final CMSSignedDataGenerator generator = padesCMSSignedDataBuilder.createCMSSignedDataGenerator(parameters, customContentSigner, signerInfoGeneratorBuilder, null); final CMSProcessableByteArray content = new CMSProcessableByteArray(messageDigest); DSSASN1Utils.generateCMSSignedData(generator, content, false); final byte[] dataToSign = customContentSigner.getOutputStream().toByteArray(); return dataToSign; } @Override public DSSDocument signDocument(final DSSDocument toSignDocument, final SignatureParameters parameters, final byte[] signatureValue) throws DSSException { assertSigningDateInCertificateValidityRange(parameters); try { final SignatureAlgorithm signatureAlgorithm = parameters.getSignatureAlgorithm(); final CustomContentSigner customContentSigner = new CustomContentSigner(signatureAlgorithm.getJCEId(), signatureValue); final PDFSignatureService pdfSignatureService = PdfObjFactory.getInstance().newPAdESSignatureService(); InputStream inputStream = toSignDocument.openStream(); final byte[] messageDigest = pdfSignatureService.digest(inputStream, parameters, parameters.getDigestAlgorithm()); DSSUtils.closeQuietly(inputStream); final SignerInfoGeneratorBuilder signerInfoGeneratorBuilder = padesCMSSignedDataBuilder.getSignerInfoGeneratorBuilder(parameters, messageDigest); final CMSSignedDataGenerator generator = padesCMSSignedDataBuilder.createCMSSignedDataGenerator(parameters, customContentSigner, signerInfoGeneratorBuilder, null); final CMSProcessableByteArray content = new CMSProcessableByteArray(messageDigest); final boolean encapsulate = false; CMSSignedData data = generator.generate(content, encapsulate); final SignatureLevel signatureLevel = parameters.getSignatureLevel(); if (signatureLevel != SignatureLevel.PAdES_BASELINE_B) { // use an embedded timestamp CAdESLevelBaselineT cadesLevelBaselineT = new CAdESLevelBaselineT(tspSource, certificateVerifier, false); data = cadesLevelBaselineT.extendCMSSignatures(data, parameters); } final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); final byte[] encodedData = DSSASN1Utils.getEncoded(data); inputStream = toSignDocument.openStream(); pdfSignatureService.sign(inputStream, encodedData, byteArrayOutputStream, parameters, parameters.getDigestAlgorithm()); DSSUtils.closeQuietly(inputStream); final DSSDocument signature; if (DSSUtils.isEmpty(toSignDocument.getName())) { signature = new InMemoryDocument(byteArrayOutputStream.toByteArray(), null, MimeType.PDF); } else { signature = new InMemoryDocument(byteArrayOutputStream.toByteArray(), toSignDocument.getName(), MimeType.PDF); } final SignatureExtension extension = getExtensionProfile(parameters); if (signatureLevel != SignatureLevel.PAdES_BASELINE_B && signatureLevel != SignatureLevel.PAdES_BASELINE_T && extension != null) { final DSSDocument extendSignature = extension.extendSignatures(signature, parameters); parameters.setDeterministicId(null); return extendSignature; } else { parameters.setDeterministicId(null); return signature; } } catch (CMSException e) { throw new DSSException(e); } } @Override public DSSDocument extendDocument(DSSDocument toExtendDocument, SignatureParameters parameters) throws DSSException { final SignatureExtension extension = getExtensionProfile(parameters); if (extension != null) { return extension.extendSignatures(toExtendDocument, parameters); } return toExtendDocument; } @Override public DSSDocument signDocument(final DSSDocument toSignDocument, final SignatureParameters parameters) throws DSSException { final SignatureTokenConnection token = parameters.getSigningToken(); if (token == null) { throw new DSSNullException(SignatureTokenConnection.class, "", "The connection through the available API to the SSCD must be set."); } final byte[] dataToSign = getDataToSign(toSignDocument, parameters); final byte[] signatureValue = token.sign(dataToSign, parameters.getDigestAlgorithm(), parameters.getPrivateKeyEntry()); final DSSDocument dssDocument = signDocument(toSignDocument, parameters, signatureValue); return dssDocument; } }