/* * 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.IOException; import java.util.AbstractMap; import java.util.List; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import eu.europa.ec.markt.dss.DSSUtils; import eu.europa.ec.markt.dss.DigestAlgorithm; import eu.europa.ec.markt.dss.exception.DSSException; import eu.europa.ec.markt.dss.parameter.SignatureParameters; 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.pdf.PDFTimestampService; import eu.europa.ec.markt.dss.signature.pdf.PdfArray; import eu.europa.ec.markt.dss.signature.pdf.PdfDict; import eu.europa.ec.markt.dss.signature.pdf.PdfObjFactory; import eu.europa.ec.markt.dss.signature.pdf.PdfStream; import eu.europa.ec.markt.dss.validation102853.AdvancedSignature; import eu.europa.ec.markt.dss.validation102853.CertificateToken; import eu.europa.ec.markt.dss.validation102853.CertificateVerifier; import eu.europa.ec.markt.dss.validation102853.DefaultAdvancedSignature; import eu.europa.ec.markt.dss.validation102853.OCSPToken; import eu.europa.ec.markt.dss.validation102853.Token; import eu.europa.ec.markt.dss.validation102853.ValidationContext; import eu.europa.ec.markt.dss.validation102853.cades.CAdESSignature; import eu.europa.ec.markt.dss.validation102853.crl.CRLToken; import eu.europa.ec.markt.dss.validation102853.pades.PAdESSignature; import eu.europa.ec.markt.dss.validation102853.pades.PDFDocumentValidator; import eu.europa.ec.markt.dss.validation102853.tsp.TSPSource; /** * Extend a PAdES extension up to LTV. * * @version $Revision: 2723 $ - $Date: 2013-10-11 11:51:11 +0200 (Fri, 11 Oct 2013) $ */ class PAdESLevelBaselineLT implements SignatureExtension { private static final Logger LOG = LoggerFactory.getLogger(PAdESLevelBaselineLT.class); // DSSS/VRI dictionary is not mandatory, therefore it's not included private static final boolean INCLUDE_VRI_DICTIONARY = false; // the information read from the signatures final PdfObjFactory factory = PdfObjFactory.getInstance(); private PdfArray certArray = factory.newArray(); private PdfArray ocspArray = factory.newArray(); private PdfArray crlArray = factory.newArray(); private final CertificateVerifier certificateVerifier; private final TSPSource tspSource; PAdESLevelBaselineLT(final TSPSource tspSource, final CertificateVerifier certificateVerifier) { this.certificateVerifier = certificateVerifier; this.tspSource = tspSource; } /** * @param document * @param parameters * @return * @throws IOException */ @Override public InMemoryDocument extendSignatures(DSSDocument document, final SignatureParameters parameters) throws DSSException { try { // check if needed to extends with PAdESLevelBaselineT final PDFDocumentValidator pdfDocumentValidator = new PDFDocumentValidator(document); pdfDocumentValidator.setCertificateVerifier(certificateVerifier); List<AdvancedSignature> signatures = pdfDocumentValidator.getSignatures(); for (final AdvancedSignature signature : signatures) { if (!signature.isDataForSignatureLevelPresent(SignatureLevel.PAdES_BASELINE_T)) { final PAdESLevelBaselineT padesLevelBaselineT = new PAdESLevelBaselineT(tspSource, certificateVerifier); document = padesLevelBaselineT.extendSignatures(document, parameters); final PDFDocumentValidator pdfDocumentValidatorOverTimestamp = new PDFDocumentValidator(document); pdfDocumentValidatorOverTimestamp.setCertificateVerifier(certificateVerifier); signatures = pdfDocumentValidator.getSignatures(); break; } } assertExtendSignaturePossible(pdfDocumentValidator); for (final AdvancedSignature signature : signatures) { if (signature instanceof PAdESSignature) { PAdESSignature pAdESSignature = (PAdESSignature) signature; validate(pAdESSignature); } } final PdfDict dssDictionary = createDSSDictionary(); /** * Add the signature's VRI dictionary, hashing the signature block from the callback method.<br> * The key of each entry in this dictionary is the base-16-encoded (uppercase) SHA1 digest of the signature to * which it applies and the value is the Signature VRI dictionary which contains the validation-related * information for that signature. */ if (INCLUDE_VRI_DICTIONARY) { PdfDict vriDictionary = factory.newDict("VRI"); for (final AdvancedSignature signature : signatures) { if (signature instanceof PAdESSignature) { PdfDict sigVriDictionary = factory.newDict(); // sigVriDictionary to be completed with Cert, CRL and OCSP specific to this signature PAdESSignature pAdESSignature = (PAdESSignature) signature; final byte[] digest = DSSUtils.digest(DigestAlgorithm.SHA1, pAdESSignature.getCAdESSignature().getCmsSignedData().getEncoded()); String hexHash = DSSUtils.encodeHexString(digest).toUpperCase(); vriDictionary.add(hexHash, sigVriDictionary); } } dssDictionary.add("VRI", vriDictionary); // Cert, CRL and OCSP to be included } /* Baseline LT: "Hence implementations claiming conformance to the LT-Conformance Level build the PAdES-LTV form (PAdES Part 4 [9], clause 4) on signatures that shall be compliant to the T-Level requirements and to the present clause." LTA: "It is recommended that signed PDF documents, conforming to this profile, contain DSS followed by a document Time-stamp." So we add a timestamp, and that a good thing because PDFBox cannot do incremental update without signing. */ final ByteArrayOutputStream tDoc = new ByteArrayOutputStream(); final PDFTimestampService timestampService = factory.newTimestampSignatureService(); Map.Entry<String, PdfDict> dictToAdd = new AbstractMap.SimpleEntry<String, PdfDict>("DSS", dssDictionary); timestampService.timestamp(document, tDoc, parameters, tspSource, dictToAdd); final InMemoryDocument inMemoryDocument = new InMemoryDocument(tDoc.toByteArray()); inMemoryDocument.setMimeType(MimeType.PDF); return inMemoryDocument; } catch (IOException e) { throw new DSSException(e); } } private PdfDict createDSSDictionary() throws IOException { final PdfDict dssDictionary = factory.newDict("DSS"); if (certArray.size() > 0) { dssDictionary.add("Certs", certArray); } if (crlArray.size() > 0) { dssDictionary.add("CRLs", crlArray); } if (ocspArray.size() > 0) { dssDictionary.add("OCSPs", ocspArray); } return dssDictionary; } private void assertExtendSignaturePossible(PDFDocumentValidator pdfDocumentValidator) { } private void validate(final PAdESSignature pAdESSignature) { final CAdESSignature cadesSignature = pAdESSignature.getCAdESSignature(); final ValidationContext validationContext = cadesSignature.getSignatureValidationContext(certificateVerifier); final DefaultAdvancedSignature.RevocationDataForInclusion revocationsForInclusionInProfileLT = cadesSignature.getRevocationDataForInclusion(validationContext); for (final CRLToken crlToken : revocationsForInclusionInProfileLT.crlTokens) { addNewToken(crlToken, crlArray); } for (final OCSPToken ocspToken : revocationsForInclusionInProfileLT.ocspTokens) { addNewToken(ocspToken, ocspArray); } final Set<CertificateToken> certificatesForInclusionInProfileLT = cadesSignature.getCertificatesForInclusion(validationContext); for (final CertificateToken certificateToken : certificatesForInclusionInProfileLT) { addNewToken(certificateToken, certArray); } } private void addNewToken(final Token crlToken, final PdfArray pdfArray) throws DSSException { try { final PdfStream stream = factory.newStream(crlToken.getEncoded()); pdfArray.add(stream); } catch (IOException e) { throw new DSSException(e); } } }