eu.europa.ec.markt.dss.signature.pades.PAdESProfileLTV.java Source code

Java tutorial

Introduction

Here is the source code for eu.europa.ec.markt.dss.signature.pades.PAdESProfileLTV.java

Source

/*
 * DSS - Digital Signature Services
 *
 * Copyright (C) 2011 European Commission, Directorate-General Internal Market and Services (DG MARKT), B-1049 Bruxelles/Brussel
 *
 * Developed by: 2011 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 eu.europa.ec.markt.dss.DigestAlgorithm;
import eu.europa.ec.markt.dss.NotETSICompliantException;
import eu.europa.ec.markt.dss.NotETSICompliantException.MSG;
import eu.europa.ec.markt.dss.signature.Document;
import eu.europa.ec.markt.dss.signature.InMemoryDocument;
import eu.europa.ec.markt.dss.signature.SignatureExtension;
import eu.europa.ec.markt.dss.signature.SignatureParameters;
import eu.europa.ec.markt.dss.signature.pdf.ITextPDFDocTimeSampService;
import eu.europa.ec.markt.dss.signature.pdf.ITextPDFSignatureService;
import eu.europa.ec.markt.dss.signature.pdf.PDFSignatureService;
import eu.europa.ec.markt.dss.signature.pdf.SignatureValidationCallback;
import eu.europa.ec.markt.dss.validation.CertificateVerifier;
import eu.europa.ec.markt.dss.validation.ValidationContext;
import eu.europa.ec.markt.dss.validation.cades.CAdESSignature;
import eu.europa.ec.markt.dss.validation.certificate.CertificateAndContext;
import eu.europa.ec.markt.dss.validation.ocsp.OCSPUtils;
import eu.europa.ec.markt.dss.validation.tsp.TSPSource;
import eu.europa.ec.markt.dss.validation.x509.TimestampToken;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.security.cert.CRLException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;

import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.ocsp.BasicOCSPResp;
import org.bouncycastle.tsp.TimeStampResponse;

import com.lowagie.text.DocumentException;
import com.lowagie.text.pdf.PdfArray;
import com.lowagie.text.pdf.PdfDate;
import com.lowagie.text.pdf.PdfDeveloperExtension;
import com.lowagie.text.pdf.PdfDictionary;
import com.lowagie.text.pdf.PdfIndirectReference;
import com.lowagie.text.pdf.PdfName;
import com.lowagie.text.pdf.PdfPKCS7;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfStamper;
import com.lowagie.text.pdf.PdfStream;

/**
 * Extend a PAdES extension up to LTV.
 * 
 * 
 * @version $Revision: 1867 $ - $Date: 2013-04-08 13:44:56 +0200 (Mon, 08 Apr 2013) $
 */

public class PAdESProfileLTV implements SignatureExtension {

    private PDFSignatureService pdfSignatureService = new ITextPDFSignatureService();

    private CertificateVerifier certificateVerifier;

    private Map<X509Certificate, PdfIndirectReference> certsRefs = new HashMap<X509Certificate, PdfIndirectReference>();
    private Map<X509CRL, PdfIndirectReference> crlRefs = new HashMap<X509CRL, PdfIndirectReference>();
    private Map<BasicOCSPResp, PdfIndirectReference> ocspRefs = new HashMap<BasicOCSPResp, PdfIndirectReference>();

    private TSPSource tspSource;

    /**
     * @param tspSource the tspSource to set
     */
    public void setTspSource(TSPSource tspSource) {
        this.tspSource = tspSource;
    }

    /**
     * @param certificateVerifier the certificateVerifier to set
     */
    public void setCertificateVerifier(CertificateVerifier certificateVerifier) {
        this.certificateVerifier = certificateVerifier;
    }

    class LTVSignatureValidationCallback implements SignatureValidationCallback {

        private PdfStamper stamper;

        private PdfArray certsArray = new PdfArray();

        private PdfArray ocspsArray = new PdfArray();

        private PdfArray crlsArray = new PdfArray();

        private ValidationContext validationContext;

        private byte[] signatureBlock;

        public LTVSignatureValidationCallback(PdfStamper stamper) {
            this.stamper = stamper;
        }

        @Override
        public void validate(PdfReader reader, PdfDictionary outerCatalog, X509Certificate signingCert,
                Date signingDate, Certificate[] certs, PdfDictionary signatureDictionary, PdfPKCS7 pk) {

            if (signingCert == null) {
                throw new NotETSICompliantException(MSG.NO_SIGNING_CERTIFICATE);
            }

            if (signingDate == null) {
                throw new NotETSICompliantException(MSG.NO_SIGNING_TIME);
            }

            try {

                this.signatureBlock = signatureDictionary.get(PdfName.CONTENTS).getBytes();

                CAdESSignature cades = new CAdESSignature(signatureBlock);
                final ValidationContext ctx = certificateVerifier.validateCertificate(signingCert, signingDate,
                        cades.getCertificateSource(), null, null);
                if (cades.getSignatureTimestamps() != null) {
                    for (TimestampToken tstoken : cades.getSignatureTimestamps()) {
                        ctx.validateTimestamp(tstoken, cades.getCertificateSource(), null, null);
                    }
                }

                for (BasicOCSPResp ocsp : ctx.getNeededOCSPResp()) {
                    try {
                        PdfIndirectReference cRef = stamper.getWriter().getPdfIndirectReference();
                        PdfStream stream = new PdfStream(OCSPUtils.fromBasicToResp(ocsp).getEncoded());
                        stamper.getWriter().addToBody(stream, cRef, false);
                        ocspsArray.add(cRef);
                        ocspRefs.put(ocsp, cRef);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }

                for (X509CRL crl : ctx.getNeededCRL()) {
                    try {
                        PdfIndirectReference cRef = stamper.getWriter().getPdfIndirectReference();
                        PdfStream stream = new PdfStream(crl.getEncoded());
                        stamper.getWriter().addToBody(stream, cRef, false);
                        crlsArray.add(cRef);
                        crlRefs.put(crl, cRef);
                    } catch (CRLException e) {
                        throw new RuntimeException(e);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }

                for (CertificateAndContext cert : ctx.getNeededCertificates()) {
                    try {
                        PdfIndirectReference cRef = stamper.getWriter().getPdfIndirectReference();
                        PdfStream stream = new PdfStream(cert.getCertificate().getEncoded());
                        stamper.getWriter().addToBody(stream, cRef, false);
                        certsArray.add(cRef);
                        certsRefs.put(cert.getCertificate(), cRef);
                    } catch (CertificateEncodingException e) {
                        throw new RuntimeException(e);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            } catch (CMSException e) {
                throw new RuntimeException(e);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        /**
         * @return the certsArray
         */
        public PdfArray getCertsArray() {
            return certsArray;
        }

        /**
         * @return the crlsArray
         */
        public PdfArray getCrlsArray() {
            return crlsArray;
        }

        /**
         * @return the ocspsArray
         */
        public PdfArray getOcspsArray() {
            return ocspsArray;
        }

        /**
         * @return the signatureBlock
         */
        public byte[] getSignatureBlock() {
            return signatureBlock;
        }

        /**
         * @return the validationContext
         */
        public ValidationContext getValidationContext() {
            return validationContext;
        }

    }

    private PdfIndirectReference buildVRIDict(PdfStamper stamper, BasicOCSPResp crl) throws IOException {
        PdfIndirectReference ref = stamper.getWriter().getPdfIndirectReference();
        PdfDictionary ocspVriDictionary = new PdfDictionary();
        PdfDate vriDate = new PdfDate(Calendar.getInstance(TimeZone.getTimeZone("GMT")));
        ocspVriDictionary.put(new PdfName("TU"), vriDate);

        // Other objects?

        stamper.getWriter().addToBody(ocspVriDictionary, ref, false);
        return ref;
    }

    private PdfIndirectReference buildVRIDict(PdfStamper stamper, X509CRL crl) throws IOException {
        PdfIndirectReference ref = stamper.getWriter().getPdfIndirectReference();
        PdfDictionary crlVriDictionary = new PdfDictionary();
        PdfDate vriDate = new PdfDate(Calendar.getInstance(TimeZone.getTimeZone("GMT")));
        crlVriDictionary.put(new PdfName("TU"), vriDate);

        // Other objects?

        stamper.getWriter().addToBody(crlVriDictionary, ref, false);
        return ref;
    }

    private void integrateCRL(LTVSignatureValidationCallback callback, PdfStamper stamper,
            PdfDictionary dssDictionary, PdfDictionary sigVriDictionary, PdfDictionary vriDictionary)
            throws IOException {
        if (callback.getCrlsArray().size() > 0) {
            // Reference in the DSS dictionary
            PdfIndirectReference crlsRef = stamper.getWriter().getPdfIndirectReference();
            stamper.getWriter().addToBody(callback.getCrlsArray(), crlsRef, false);
            dssDictionary.put(new PdfName("CRLs"), crlsRef);

            // Array in the signature's VRI dictionary
            PdfIndirectReference sigVriCrlRef = stamper.getWriter().getPdfIndirectReference();
            stamper.getWriter().addToBody(callback.getCrlsArray(), sigVriCrlRef, false);
            sigVriDictionary.put(new PdfName("CRL"), sigVriCrlRef);

            // Build and reference a VRI dictionary for each CRL
            for (X509CRL crl : crlRefs.keySet()) {
                try {
                    PdfIndirectReference vriRef = buildVRIDict(stamper, crl);
                    MessageDigest md = MessageDigest.getInstance(DigestAlgorithm.SHA1.getName());
                    String hexHash = Hex.encodeHexString(md.digest(crl.getSignature())).toUpperCase();
                    vriDictionary.put(new PdfName(hexHash), vriRef);
                } catch (NoSuchAlgorithmException e) {
                    throw new RuntimeException();
                }
            }
        }
    }

    private void integrateOCSP(LTVSignatureValidationCallback callback, PdfStamper stamper,
            PdfDictionary dssDictionary, PdfDictionary sigVriDictionary, PdfDictionary vriDictionary)
            throws IOException {
        if (callback.getOcspsArray().size() > 0) {
            // Reference in the DSS dictionary
            PdfIndirectReference ocspsRef = stamper.getWriter().getPdfIndirectReference();
            stamper.getWriter().addToBody(callback.getOcspsArray(), ocspsRef, false);
            dssDictionary.put(new PdfName("OCSPs"), ocspsRef);

            // Array in the signature's VRI dictionary
            PdfIndirectReference sigVriOcspRef = stamper.getWriter().getPdfIndirectReference();
            stamper.getWriter().addToBody(callback.getOcspsArray(), sigVriOcspRef, false);
            sigVriDictionary.put(new PdfName("OCSP"), sigVriOcspRef);

            // Build and reference a VRI dictionary for each OCSP response
            for (BasicOCSPResp ocsp : ocspRefs.keySet()) {
                try {
                    PdfIndirectReference vriRef = buildVRIDict(stamper, ocsp);
                    MessageDigest md = MessageDigest.getInstance(DigestAlgorithm.SHA1.getName());
                    String hexHash = Hex.encodeHexString(md.digest(ocsp.getSignature())).toUpperCase();
                    vriDictionary.put(new PdfName(hexHash), vriRef);
                } catch (NoSuchAlgorithmException e) {
                    throw new RuntimeException();
                }
            }
        }
    }

    @Override
    public Document extendSignatures(Document document, Document originalData, SignatureParameters parameters)
            throws IOException {

        try {
            final PdfReader reader = new PdfReader(document.openStream());
            final ByteArrayOutputStream output = new ByteArrayOutputStream();
            final PdfStamper stamper = new PdfStamper(reader, output, '\0', true);

            LTVSignatureValidationCallback callback = new LTVSignatureValidationCallback(stamper);
            pdfSignatureService.validateSignatures(document.openStream(), callback);

            PdfIndirectReference certsRef = stamper.getWriter().getPdfIndirectReference();
            stamper.getWriter().addToBody(callback.getCertsArray(), certsRef, false);

            PdfDictionary dssDictionary = new PdfDictionary(new PdfName("DSS"));
            PdfDictionary vriDictionary = new PdfDictionary(new PdfName("VRI"));

            PdfDictionary sigVriDictionary = new PdfDictionary();

            integrateCRL(callback, stamper, dssDictionary, sigVriDictionary, sigVriDictionary);

            integrateOCSP(callback, stamper, dssDictionary, sigVriDictionary, sigVriDictionary);

            // Add the signature's VRI dictionary, hashing the signature block from the callback method
            MessageDigest _md = MessageDigest.getInstance(DigestAlgorithm.SHA1.getName());
            String hexHash = Hex.encodeHexString(_md.digest(callback.getSignatureBlock())).toUpperCase();

            PdfIndirectReference sigVriRef = stamper.getWriter().getPdfIndirectReference();
            stamper.getWriter().addToBody(sigVriDictionary, sigVriRef, false);
            vriDictionary.put(new PdfName(hexHash), sigVriRef);
            PdfIndirectReference vriRef = stamper.getWriter().getPdfIndirectReference();
            stamper.getWriter().addToBody(vriDictionary, vriRef, false);

            // Add final objects to DSS dictionary
            dssDictionary.put(new PdfName("VRI"), vriRef);
            dssDictionary.put(new PdfName("Certs"), certsRef);

            PdfIndirectReference dssRef = stamper.getWriter().getPdfIndirectReference();
            stamper.getWriter().addToBody(dssDictionary, dssRef, false);
            reader.getCatalog().put(new PdfName("DSS"), dssRef);

            // /Extensions<</ADBE<</BaseVersion/1.7/ExtensionLevel 5>>>>
            PdfDeveloperExtension etsiExtension = new PdfDeveloperExtension(PdfName.ADBE, new PdfName("1.7"), 5);
            stamper.getWriter().addDeveloperExtension(etsiExtension);
            stamper.getWriter().addToBody(reader.getCatalog(), reader.getCatalog().getIndRef(), false);

            stamper.close();
            output.close();

            Document extendedDocument = new InMemoryDocument(output.toByteArray());

            ByteArrayOutputStream ltvDoc = new ByteArrayOutputStream();

            ITextPDFDocTimeSampService service = new ITextPDFDocTimeSampService();
            byte[] digest = service.digest(extendedDocument.openStream(), parameters);
            TimeStampResponse tsToken = tspSource.getTimeStampResponse(parameters.getDigestAlgorithm(), digest);
            service.sign(extendedDocument.openStream(), tsToken.getTimeStampToken().getEncoded(), ltvDoc,
                    parameters);

            return new InMemoryDocument(ltvDoc.toByteArray());

        } catch (DocumentException ex) {
            throw new RuntimeException(ex);
        } catch (SignatureException e) {
            throw new RuntimeException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }

    }

    @Override
    public Document extendSignature(Object signatureId, Document document, Document originalData,
            SignatureParameters parameters) throws IOException {
        /* On PAdES, we retrieve the data for all the signatures */
        return extendSignatures(document, originalData, parameters);
    }

}