org.opencps.pki.PdfSigner.java Source code

Java tutorial

Introduction

Here is the source code for org.opencps.pki.PdfSigner.java

Source

/**
* OpenCPS PKI is the open source PKI Integration software
* Copyright (C) 2016-present OpenCPS community
    
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
    
* This program 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 Affero General Public License for more details.
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package org.opencps.pki;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.SignatureException;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Calendar;

import javax.imageio.ImageIO;

import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfName;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfSignatureAppearance;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.security.BouncyCastleDigest;
import com.itextpdf.text.pdf.security.CertificateUtil;
import com.itextpdf.text.pdf.security.DigestAlgorithms;
import com.itextpdf.text.pdf.security.ExternalBlankSignatureContainer;
import com.itextpdf.text.pdf.security.ExternalDigest;
import com.itextpdf.text.pdf.security.ExternalSignatureContainer;
import com.itextpdf.text.pdf.security.LtvTimestamp;
import com.itextpdf.text.pdf.security.MakeSignature;
import com.itextpdf.text.pdf.security.MakeSignature.CryptoStandard;
import com.itextpdf.text.pdf.security.PdfPKCS7;
import com.itextpdf.text.pdf.security.TSAClient;
import com.itextpdf.text.pdf.security.TSAClientBouncyCastle;

/**
 * Signer for pdf document
 * @author Nguyen Van Nguyen <nguyennv@iwayvietnam.com>
 */
public class PdfSigner extends BaseSigner {

    /**
     * Signature image
     */
    private SignatureImage signatureImage;

    /**
     * signature field name
     */
    private String signatureFieldName;

    /**
     * Sign date
     */
    private Calendar signDate;

    /**
     * Signature is visible
     */
    private Boolean isVisible;

    /**
     * External digest
     */
    private ExternalDigest digest;

    /**
     * Constructor
     *
     * @param filePath The path of pdf document
     * @param cert The certificate of user
     */
    public PdfSigner(String filePath, X509Certificate cert) {
        this(filePath, cert, Helper.stripFileExtension(filePath) + ".temp.pdf",
                Helper.stripFileExtension(filePath) + ".signed.pdf", true);
    }

    /**
     * Constructor
     *
     * @param filePath The path of pdf document
     * @param cert The certificate of user
     * @param tempFilePath Temporary Pdf document file path after generate hash key
     * @param signedFilePath Signed Pdf document file path
     * @param isVisible Signature is visible
     */
    public PdfSigner(String filePath, X509Certificate cert, String tempFilePath, String signedFilePath,
            boolean isVisible) {
        super(filePath, cert, tempFilePath, signedFilePath);
        this.isVisible = isVisible;
        signDate = Calendar.getInstance();
        digest = new BouncyCastleDigest();
    }

    /**
     * (non-Javadoc)
     * @throws SignatureException 
     * @see org.opencps.pki.Signer#computeHash()
     */
    @Override
    public byte[] computeHash() throws SignatureException {
        float llx = 36.0f;
        float lly = 48.0f;
        if (signatureImage != null) {
            int signatureImageWidth = signatureImage.getBufferedImage().getWidth();
            int signatureImageHeight = signatureImage.getBufferedImage().getHeight();
            float urx = llx + signatureImageWidth;
            float ury = lly + signatureImageHeight;
            return computeHash(llx, lly, urx, ury);
        } else {
            return computeHash(llx, lly, 144, 80);
        }
    }

    /**
     * Compute hash key with corner coordinates of rectangle
     *
     * @param llx lower left x
     * @param lly lower left y
     * @param urx upper right x
     * @param ury upper right y
     * @throws SignatureException 
     */
    public byte[] computeHash(float llx, float lly, float urx, float ury) throws SignatureException {
        try {
            byte[] digestHash = computeDigest(llx, lly, urx, ury);
            PdfPKCS7 sgn = new PdfPKCS7(null, new Certificate[] { getCertificate() }, getHashAlgorithm().toString(),
                    null, digest, false);
            return sgn.getAuthenticatedAttributeBytes(digestHash, null, null, CryptoStandard.CMS);
        } catch (Exception e) {
            throw new SignatureException(e.getMessage(), e);
        }
    }

    /**
     * (non-Javadoc)
     * @throws SignatureException 
     * @see org.opencps.pki.Signer#sign()
     */
    @Override
    public Boolean sign(byte[] signature) throws SignatureException {
        return sign(signature, getTempFilePath());
    }

    /**
     * (non-Javadoc)
     * @throws SignatureException 
     * @see org.opencps.pki.Signer#sign()
     */
    @Override
    public Boolean sign(byte[] signature, String filePath) throws SignatureException {
        return signExternal(new Pkcs7GenerateSignatureContainer(this, signature), filePath);
    }

    /**
     * Get sign date
     */
    public Calendar getSignDate() {
        return signDate;
    }

    /**
     * Set sign date
     */
    public PdfSigner setSignDate(Calendar signDate) {
        this.signDate = signDate;
        return this;
    }

    /**
     * Get signature is visible
     */
    public Boolean getIsVisible() {
        return isVisible;
    }

    /**
     * Set signature is visible
     */
    public PdfSigner setIsVisible(Boolean isVisible) {
        this.isVisible = isVisible;
        return this;
    }

    /**
     * Set signature graphic
     */
    public PdfSigner setSignatureGraphic(String imagePath) {
        signatureImage = new SignatureImage(imagePath);
        return this;
    }

    /**
     * Get signature graphic
     */
    public SignatureImage getSignatureGraphic() {
        return signatureImage;
    }

    /**
     * Get signature field name pdf document
     */
    public String getSignatureFieldName() {
        return signatureFieldName;
    }

    public PdfSigner setSignatureFieldName(String fieldName) {
        signatureFieldName = fieldName;
        return this;
    }

    /**
     * Get external digest
     */
    public ExternalDigest getExternalDigest() {
        return digest;
    }

    /**
     * Compute digest hash
     */
    protected byte[] computeDigest(float llx, float lly, float urx, float ury) throws SignatureException {
        byte digestHash[] = null;
        int contentEstimated = 8192;
        try {
            PdfReader reader = new PdfReader(getOriginFilePath());
            FileOutputStream os = new FileOutputStream(getTempFilePath());
            PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');
            PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
            signatureFieldName = appearance.getNewSigName();
            TSAClient tsaClient = null;
            appearance.setCertificate(getCertificate());
            String tsaUrl = CertificateUtil.getTSAURL(getCertificate());
            if (tsaUrl != null) {
                tsaClient = new TSAClientBouncyCastle(tsaUrl);
            }
            if (tsaClient != null) {
                LtvTimestamp.timestamp(appearance, tsaClient, signatureFieldName);
                contentEstimated += 4096;
            }

            appearance.setSignDate(signDate);
            CertificateInfo certInfo = new CertificateInfo(getCertificate());
            appearance.setLocation(certInfo.getOrganizationUnit());
            appearance.setReason("Document is signed by " + certInfo.getCommonName());
            appearance.setContact(certInfo.getCommonName());
            if (!isVisible) {
                appearance.setVisibleSignature(new Rectangle(0, 0, 0, 0), 1, signatureFieldName);
            } else {
                if (signatureImage != null) {
                    appearance.setSignatureGraphic(signatureImage.getImage());
                    appearance.setRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC);
                } else {
                    appearance.setLayer2Text(certInfo.getCommonName());
                }
                appearance.setVisibleSignature(new Rectangle(llx, lly, urx, ury), 1, signatureFieldName);
            }

            ExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE,
                    PdfName.ADBE_PKCS7_DETACHED);
            MakeSignature.signExternalContainer(appearance, external, contentEstimated);

            digestHash = DigestAlgorithms.digest(appearance.getRangeStream(),
                    digest.getMessageDigest(getHashAlgorithm().toString()));

            reader.close();
            os.close();
        } catch (Exception e) {
            throw new SignatureException(e.getMessage(), e);
        }
        return digestHash;
    }

    /**
     * Sign document with external signature container
     */
    protected Boolean signExternal(ExternalSignatureContainer container, String filePath)
            throws SignatureException {
        if (signatureFieldName == null || signatureFieldName.length() == 0) {
            throw new SignatureException("You must set signature field name before sign the document");
        }
        Boolean signed = false;
        try {
            OutputStream os = new FileOutputStream(getSignedFilePath());
            PdfReader reader = new PdfReader(filePath);
            if (!reader.isEncrypted()) {
                MakeSignature.signDeferred(reader, signatureFieldName, os, container);
                reader.close();
                os.close();
                signed = true;
            }
        } catch (Exception e) {
            throw new SignatureException(e.getMessage(), e);
        }
        return signed;
    }

    /**
     * Signature image class
     */
    class SignatureImage {
        private BufferedImage bufferedImage;

        private Image image;

        /**
         * Constructor
         */
        public SignatureImage(String imagePath) {
            try {
                image = Image.getInstance(imagePath);
                InputStream is = new FileInputStream(new File(imagePath));
                bufferedImage = ImageIO.read(is);
            } catch (Exception e) {
                throw new RuntimeException(e.getMessage(), e);
            }
        }

        /**
         * Get buffered image object
         */
        public BufferedImage getBufferedImage() {
            return bufferedImage;
        }

        /**
         * Get image object
         */
        public Image getImage() {
            return image;
        }
    }
}