/** * 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.validation; import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Set; 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.DigestAlgorithm; import eu.europa.esig.dss.EncryptionAlgorithm; import eu.europa.esig.dss.MimeType; import eu.europa.esig.dss.SignatureForm; import eu.europa.esig.dss.SignatureLevel; import eu.europa.esig.dss.cades.validation.CAdESSignature; import eu.europa.esig.dss.pdf.PdfDocTimestampInfo; import eu.europa.esig.dss.pdf.PdfDssDict; import eu.europa.esig.dss.pdf.PdfSignatureInfo; import eu.europa.esig.dss.pdf.PdfSignatureOrDocTimestampInfo; import eu.europa.esig.dss.utils.Utils; import eu.europa.esig.dss.validation.AdvancedSignature; import eu.europa.esig.dss.validation.CRLRef; import eu.europa.esig.dss.validation.CandidatesForSigningCertificate; import eu.europa.esig.dss.validation.CertificateRef; import eu.europa.esig.dss.validation.CertifiedRole; import eu.europa.esig.dss.validation.CommitmentType; import eu.europa.esig.dss.validation.OCSPRef; import eu.europa.esig.dss.validation.SignatureProductionPlace; import eu.europa.esig.dss.validation.TimestampReference; import eu.europa.esig.dss.validation.TimestampReferenceCategory; import eu.europa.esig.dss.validation.TimestampToken; import eu.europa.esig.dss.x509.CertificatePool; import eu.europa.esig.dss.x509.CertificateToken; import eu.europa.esig.dss.x509.SignaturePolicy; import eu.europa.esig.dss.x509.TimestampType; import eu.europa.esig.dss.x509.crl.OfflineCRLSource; import eu.europa.esig.dss.x509.ocsp.OfflineOCSPSource; /** * Implementation of AdvancedSignature for PAdES */ public class PAdESSignature extends CAdESSignature { private static final Logger logger = LoggerFactory.getLogger(PAdESSignature.class); private final DSSDocument document; private final PdfDssDict dssDictionary; private final PdfSignatureInfo pdfSignatureInfo; private PAdESCertificateSource padesCertSources; /** * The default constructor for PAdESSignature. * * @param document * @param pdfSignatureInfo * @param certPool * @throws DSSException */ protected PAdESSignature(final DSSDocument document, final PdfSignatureInfo pdfSignatureInfo, final CertificatePool certPool) throws DSSException { super(pdfSignatureInfo.getCades().getCmsSignedData(), certPool, pdfSignatureInfo.getCades().getDetachedContents()); this.document = document; this.dssDictionary = pdfSignatureInfo.getDssDictionary(); this.pdfSignatureInfo = pdfSignatureInfo; } @Override public SignatureForm getSignatureForm() { return SignatureForm.PAdES; } @Override public EncryptionAlgorithm getEncryptionAlgorithm() { return super.getEncryptionAlgorithm(); } @Override public DigestAlgorithm getDigestAlgorithm() { return super.getDigestAlgorithm(); } @Override public PAdESCertificateSource getCertificateSource() { if (padesCertSources == null) { padesCertSources = new PAdESCertificateSource(dssDictionary, super.getCmsSignedData(), super.getSignerInformation(), certPool); } return padesCertSources; } @Override public OfflineCRLSource getCRLSource() { if (offlineCRLSource == null) { offlineCRLSource = new PAdESCRLSource(dssDictionary); } return offlineCRLSource; } @Override public OfflineOCSPSource getOCSPSource() { if (offlineOCSPSource == null) { offlineOCSPSource = new PAdESOCSPSource(dssDictionary); } return offlineOCSPSource; } @Override public CandidatesForSigningCertificate getCandidatesForSigningCertificate() { return super.getCandidatesForSigningCertificate(); } @Override public Date getSigningTime() { return pdfSignatureInfo.getSigningDate(); } @Override public SignaturePolicy getPolicyId() { return super.getPolicyId(); } @Override public SignatureProductionPlace getSignatureProductionPlace() { String location = pdfSignatureInfo.getLocation(); if (Utils.isStringBlank(location)) { return super.getSignatureProductionPlace(); } else { SignatureProductionPlace signatureProductionPlace = new SignatureProductionPlace(); signatureProductionPlace.setCountryName(location); return signatureProductionPlace; } } @Override public String getContentType() { return MimeType.PDF.getMimeTypeString(); } @Override public String getContentIdentifier() { return null; } @Override public String getContentHints() { return null; } @Override public String[] getClaimedSignerRoles() { return super.getClaimedSignerRoles(); } @Override public List<CertifiedRole> getCertifiedSignerRoles() { return null; } @Override public List<TimestampToken> getContentTimestamps() { return super.getContentTimestamps(); } @Override public byte[] getContentTimestampData(final TimestampToken timestampToken) { return super.getContentTimestampData(timestampToken); } @Override public List<TimestampToken> getSignatureTimestamps() { final List<TimestampToken> result = new ArrayList<TimestampToken>(); result.addAll(super.getSignatureTimestamps()); final Set<PdfSignatureOrDocTimestampInfo> outerSignatures = pdfSignatureInfo.getOuterSignatures(); for (final PdfSignatureOrDocTimestampInfo outerSignature : outerSignatures) { if (outerSignature.isTimestamp() && (outerSignature instanceof PdfDocTimestampInfo)) { final PdfDocTimestampInfo pdfBoxTimestampInfo = (PdfDocTimestampInfo) outerSignature; // do not return this timestamp if it's an archive timestamp final TimestampToken timestampToken = pdfBoxTimestampInfo.getTimestampToken(); if (timestampToken.getTimeStampType() == TimestampType.SIGNATURE_TIMESTAMP) { timestampToken.setTimestampedReferences(getSignatureTimestampedReferences()); result.add(timestampToken); } } } return Collections.unmodifiableList(result); } @Override public List<TimestampToken> getTimestampsX1() { /* Not applicable for PAdES */ return Collections.emptyList(); } @Override public List<TimestampToken> getTimestampsX2() { /* Not applicable for PAdES */ return Collections.emptyList(); } @Override public List<TimestampToken> getArchiveTimestamps() { final List<TimestampToken> archiveTimestampTokenList = new ArrayList<TimestampToken>(); final List<String> timestampedTimestamps = new ArrayList<String>(); final Set<PdfSignatureOrDocTimestampInfo> outerSignatures = pdfSignatureInfo.getOuterSignatures(); usedCertificatesDigestAlgorithms.add(DigestAlgorithm.SHA1); for (TimestampToken token : super.getSignatureTimestamps()) { timestampedTimestamps.add(token.getDSSIdAsString()); } for (final PdfSignatureOrDocTimestampInfo outerSignature : outerSignatures) { if (outerSignature.isTimestamp()) { PdfDocTimestampInfo pdfBoxTimestampInfo = (PdfDocTimestampInfo) outerSignature; // return this timestamp if it's an archive timestamp final TimestampToken timestampToken = pdfBoxTimestampInfo.getTimestampToken(); if (timestampToken.getTimeStampType() == TimestampType.ARCHIVE_TIMESTAMP) { final List<TimestampReference> references = getSignatureTimestampedReferences(); for (final String timestampId : timestampedTimestamps) { references.add(new TimestampReference(timestampId, TimestampReferenceCategory.TIMESTAMP)); } final List<CertificateRef> certRefs = getCertificateRefs(); for (final CertificateRef certRef : certRefs) { references.add(createCertificateTimestampReference(certRef)); } addReferencesFromOfflineCRLSource(references); addReferencesFromOfflineOCSPSource(references); timestampToken.setTimestampedReferences(references); archiveTimestampTokenList.add(timestampToken); } timestampedTimestamps.add(timestampToken.getDSSIdAsString()); } } return Collections.unmodifiableList(archiveTimestampTokenList); } @Override public List<TimestampReference> getSignatureTimestampedReferences() { final List<TimestampReference> references = new ArrayList<TimestampReference>(); // timestamp of the current signature references.add(new TimestampReference(getId())); // retrieve references from CMS Object final List<TimestampReference> signingCertificateTimestampReferences = super.getSigningCertificateTimestampReferences(); for (TimestampReference timestampReference : signingCertificateTimestampReferences) { usedCertificatesDigestAlgorithms.add(timestampReference.getDigestAlgorithm()); } references.addAll(signingCertificateTimestampReferences); return references; } private TimestampReference createCertificateTimestampReference(CertificateRef ref) { usedCertificatesDigestAlgorithms.add(ref.getDigestAlgorithm()); return new TimestampReference(ref.getDigestAlgorithm(), Utils.toBase64(ref.getDigestValue()), TimestampReferenceCategory.CERTIFICATE); } @Override public List<CertificateToken> getCertificates() { return getCertificateSource().getCertificates(); } @Override public void checkSignatureIntegrity() { super.checkSignatureIntegrity(); } @Override public void checkSigningCertificate() { // TODO-Bob (13/07/2014): } @Override public List<AdvancedSignature> getCounterSignatures() { /* Not applicable for PAdES */ return Collections.emptyList(); } @Override public List<CertificateRef> getCertificateRefs() { List<CertificateRef> refs = new ArrayList<CertificateRef>(); if (dssDictionary != null) { Set<CertificateToken> certList = dssDictionary.getCertList(); for (CertificateToken certificateToken : certList) { CertificateRef ref = new CertificateRef(); ref.setDigestAlgorithm(DigestAlgorithm.SHA1); ref.setDigestValue(certificateToken.getDigest(DigestAlgorithm.SHA1)); refs.add(ref); } } return refs; } @Override public List<CRLRef> getCRLRefs() { return Collections.emptyList(); } @Override public List<OCSPRef> getOCSPRefs() { return Collections.emptyList(); } @Override public byte[] getSignatureTimestampData(final TimestampToken timestampToken, String canonicalizationMethod) { if (super.getSignatureTimestamps().contains(timestampToken)) { return super.getSignatureTimestampData(timestampToken, null); } else { for (final PdfSignatureOrDocTimestampInfo signatureInfo : pdfSignatureInfo.getOuterSignatures()) { if (signatureInfo instanceof PdfDocTimestampInfo) { PdfDocTimestampInfo pdfTimestampInfo = (PdfDocTimestampInfo) signatureInfo; if (pdfTimestampInfo.getTimestampToken().equals(timestampToken)) { final byte[] signedDocumentBytes = pdfTimestampInfo.getSignedDocumentBytes(); return signedDocumentBytes; } } } } throw new DSSException("Timestamp Data not found"); } @Override public byte[] getTimestampX1Data(final TimestampToken timestampToken, String canonicalizationMethod) { /* Not applicable for PAdES */ return null; } @Override public byte[] getTimestampX2Data(final TimestampToken timestampToken, String canonicalizationMethod) { /* Not applicable for PAdES */ return null; } /** * @return the CAdES signature underlying this PAdES signature */ public CAdESSignature getCAdESSignature() { return pdfSignatureInfo.getCades(); } @Override public byte[] getArchiveTimestampData(TimestampToken timestampToken, String canonicalizationMethod) { for (final PdfSignatureOrDocTimestampInfo signatureInfo : pdfSignatureInfo.getOuterSignatures()) { if (signatureInfo instanceof PdfDocTimestampInfo) { PdfDocTimestampInfo pdfTimestampInfo = (PdfDocTimestampInfo) signatureInfo; if (pdfTimestampInfo.getTimestampToken().equals(timestampToken)) { final byte[] signedDocumentBytes = pdfTimestampInfo.getSignedDocumentBytes(); return signedDocumentBytes; } } } throw new DSSException("Timestamp Data not found"); } @Override public String getId() { String cadesId = super.getId(); return cadesId + getDigestOfByteRange(); } private String getDigestOfByteRange() { int[] signatureByteRange = pdfSignatureInfo.getSignatureByteRange(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); for (int i : signatureByteRange) { baos.write(i); } return DSSUtils.getMD5Digest(baos.toByteArray()); } @Override public List<TimestampReference> getTimestampedReferences() { /* Not applicable for PAdES */ return Collections.emptyList(); } @Override public boolean isDataForSignatureLevelPresent(SignatureLevel signatureLevel) { boolean dataForLevelPresent = true; switch (signatureLevel) { case PDF_NOT_ETSI: break; case PAdES_BASELINE_LTA: dataForLevelPresent = Utils.isCollectionNotEmpty(getArchiveTimestamps()); // c &= fct() will process fct() all time ; c = c && fct() will process fct() only if c is true dataForLevelPresent = dataForLevelPresent && isDataForSignatureLevelPresent(SignatureLevel.PAdES_BASELINE_LT); break; case PAdES_BASELINE_LT: dataForLevelPresent = hasDSSDictionary(); dataForLevelPresent = dataForLevelPresent && isDataForSignatureLevelPresent(SignatureLevel.PAdES_BASELINE_T); break; case PAdES_BASELINE_T: dataForLevelPresent = Utils.isCollectionNotEmpty(getSignatureTimestamps()); dataForLevelPresent = dataForLevelPresent && isDataForSignatureLevelPresent(SignatureLevel.PAdES_BASELINE_B); break; case PAdES_BASELINE_B: dataForLevelPresent = (pdfSignatureInfo != null); // && "ETSI.CAdES.detached".equals(pdfSignatureInfo.getSubFilter()); break; default: throw new IllegalArgumentException("Unknown level " + signatureLevel); } logger.debug("Level {} found on document {} = {}", new Object[] { signatureLevel, document.getName(), dataForLevelPresent }); return dataForLevelPresent; } @Override public SignatureLevel[] getSignatureLevels() { return new SignatureLevel[] { SignatureLevel.PDF_NOT_ETSI, SignatureLevel.PAdES_BASELINE_B, SignatureLevel.PAdES_BASELINE_T, SignatureLevel.PAdES_BASELINE_LT, SignatureLevel.PAdES_BASELINE_LTA }; } private boolean hasDSSDictionary() { return pdfSignatureInfo.getDssDictionary() != null; } @Override public CommitmentType getCommitmentTypeIndication() { return super.getCommitmentTypeIndication(); } public boolean hasOuterSignatures() { return Utils.isCollectionNotEmpty(pdfSignatureInfo.getOuterSignatures()); } public PdfSignatureInfo getPdfSignatureInfo() { return pdfSignatureInfo; } }