1. 程式人生 > >【itext學習之路】-------(第五篇)對pdf進行蓋章/簽章/數字簽名

【itext學習之路】-------(第五篇)對pdf進行蓋章/簽章/數字簽名

在上一篇文章中,我們學習了使用itext對pdf增加圖片水印和文字水印,那麼這篇文章我們將要學習更高階一點的水印—-印章。可能你會有疑問,印章不也是一個圖片嗎?當然,你可以把一個印章圖片來做成圖片水印,但是我們這裡要介紹的是,通過數字簽名的方式來進行pdf簽章。

  • 首選,我們要準備好jar包。
  • bcpkix-jdk15on-1.49.jar
  • bcprov-jdk15on-1.49.jar
  • itext-asian-5.2.0.jar
  • itextpdf-5.5.11-sources.jar
  • itextpdf-5.5.11.jar

    大家可以去

    maven庫中進行下載,也可以直接下載我上傳的jar包檔案:點選下載

下載好jar包之後,我們還要去了解一門技術:數字證書,而在本文中,我們需要生成一個.p12結尾的數字證書,該證書用來對我們的pdf文件進行數字簽名。生成.p12證書的方法請參考我的另一篇文章:使用JDK的keytool生成p12證書

直到這裡,我們前期的準備工作就已經全部做好,接下來,我們就要步入正題:對pdf進行簽章。

  • 首先,我們建立一個SignatureInfo的實體類,用途是為了方便的增加需要簽章的資訊:
package cn.tomtocc.pdf;
import
com.itextpdf.text.pdf.PdfSignatureAppearance; import java.security.PrivateKey; import java.security.cert.Certificate; public class SignatureInfo { private String reason; //簽名的原因,顯示在pdf簽名屬性中 private String location;//簽名的地點,顯示在pdf簽名屬性中 private String digestAlgorithm;//摘要演算法名稱,例如SHA-1 private
String imagePath;//圖章路徑 private String fieldName;//表單域名稱 private Certificate[] chain;//證書鏈 private PrivateKey pk;//簽名私鑰 private int certificationLevel = 0; //批准簽章 private PdfSignatureAppearance.RenderingMode renderingMode;//表現形式:僅描述,僅圖片,圖片和描述,簽章者和描述 //圖章屬性 private float rectllx ;//圖章左下角x private float rectlly ;//圖章左下角y private float recturx ;//圖章右上角x private float rectury ;//圖章右上角y public float getRectllx() { return rectllx; } public void setRectllx(float rectllx) { this.rectllx = rectllx; } public float getRectlly() { return rectlly; } public void setRectlly(float rectlly) { this.rectlly = rectlly; } public float getRecturx() { return recturx; } public void setRecturx(float recturx) { this.recturx = recturx; } public float getRectury() { return rectury; } public void setRectury(float rectury) { this.rectury = rectury; } public String getReason() { return reason; } public void setReason(String reason) { this.reason = reason; } public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } public String getDigestAlgorithm() { return digestAlgorithm; } public void setDigestAlgorithm(String digestAlgorithm) { this.digestAlgorithm = digestAlgorithm; } public String getImagePath() { return imagePath; } public void setImagePath(String imagePath) { this.imagePath = imagePath; } public String getFieldName() { return fieldName; } public void setFieldName(String fieldName) { this.fieldName = fieldName; } public Certificate[] getChain() { return chain; } public void setChain(Certificate[] chain) { this.chain = chain; } public PrivateKey getPk() { return pk; } public void setPk(PrivateKey pk) { this.pk = pk; } public int getCertificationLevel() { return certificationLevel; } public void setCertificationLevel(int certificationLevel) { this.certificationLevel = certificationLevel; } public PdfSignatureAppearance.RenderingMode getRenderingMode() { return renderingMode; } public void setRenderingMode(PdfSignatureAppearance.RenderingMode renderingMode) { this.renderingMode = renderingMode; } }
  • 第二步,我們建立一個用於對pdf簽章的工具類,以及main方法
package cn.tomtocc.pdf;

import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
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.DigestAlgorithms;
import com.itextpdf.text.pdf.security.ExternalDigest;
import com.itextpdf.text.pdf.security.ExternalSignature;
import com.itextpdf.text.pdf.security.MakeSignature;
import com.itextpdf.text.pdf.security.PrivateKeySignature;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.Certificate;

public class ItextUtil {

    public static final char[] PASSWORD = "123456".toCharArray();// keystory密碼

    /**
     * 單多次簽章通用
     * 
     * @param src
     * @param target
     * @param signatureInfos
     * @throws GeneralSecurityException
     * @throws IOException
     * @throws DocumentException
     */
    @SuppressWarnings("resource")
    public void sign(String src, String target, SignatureInfo signatureInfo) {
        InputStream inputStream = null;
        FileOutputStream outputStream = null;
        ByteArrayOutputStream result = new ByteArrayOutputStream();
        try {
            inputStream = new FileInputStream(src);
            ByteArrayOutputStream tempArrayOutputStream = new ByteArrayOutputStream();
            PdfReader reader = new PdfReader(inputStream);
            // 建立簽章工具PdfStamper ,最後一個boolean引數是否允許被追加簽名
            // false的話,pdf檔案只允許被簽名一次,多次簽名,最後一次有效
            // true的話,pdf可以被追加簽名,驗籤工具可以識別出每次簽名之後文件是否被修改
            PdfStamper stamper = PdfStamper.createSignature(reader,
                    tempArrayOutputStream, '\0', null, true);
            // 獲取數字簽章屬性物件
            PdfSignatureAppearance appearance = stamper
                    .getSignatureAppearance();
            appearance.setReason(signatureInfo.getReason());
            appearance.setLocation(signatureInfo.getLocation());
            // 設定簽名的位置,頁碼,簽名域名稱,多次追加簽名的時候,簽名預名稱不能一樣 圖片大小受表單域大小影響(過小導致壓縮)
            // 簽名的位置,是圖章相對於pdf頁面的位置座標,原點為pdf頁面左下角
            // 四個引數的分別是,圖章左下角x,圖章左下角y,圖章右上角x,圖章右上角y
            appearance.setVisibleSignature(
                    new Rectangle(signatureInfo.getRectllx(), signatureInfo
                            .getRectlly(), signatureInfo.getRecturx(),
                            signatureInfo.getRectury()), 1, signatureInfo
                            .getFieldName());
            // 讀取圖章圖片
            Image image = Image.getInstance(signatureInfo.getImagePath());
            appearance.setSignatureGraphic(image);
            appearance.setCertificationLevel(signatureInfo
                    .getCertificationLevel());
            // 設定圖章的顯示方式,如下選擇的是隻顯示圖章(還有其他的模式,可以圖章和簽名描述一同顯示)
            appearance.setRenderingMode(signatureInfo.getRenderingMode());
            // 摘要演算法
            ExternalDigest digest = new BouncyCastleDigest();
            // 簽名演算法
            ExternalSignature signature = new PrivateKeySignature(
                    signatureInfo.getPk(), signatureInfo.getDigestAlgorithm(),
                    null);
            // 呼叫itext簽名方法完成pdf簽章 //數字簽名格式,CMS,CADE
            MakeSignature.signDetached(appearance, digest, signature,
                    signatureInfo.getChain(), null, null, null, 0,
                    MakeSignature.CryptoStandard.CADES);

            inputStream = new ByteArrayInputStream(
                    tempArrayOutputStream.toByteArray());
            // 定義輸入流為生成的輸出流內容,以完成多次簽章的過程
            result = tempArrayOutputStream;

            outputStream = new FileOutputStream(new File(target));
            outputStream.write(result.toByteArray());
            outputStream.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != outputStream) {
                    outputStream.close();
                }
                if (null != inputStream) {
                    inputStream.close();
                }
                if (null != result) {
                    result.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        try {
            ItextUtil app = new ItextUtil();
            // 將證書檔案放入指定路徑,並讀取keystore ,獲得私鑰和證書鏈
            String pkPath = "D:/server.p12";
            KeyStore ks = KeyStore.getInstance("PKCS12");
            ks.load(new FileInputStream(pkPath), PASSWORD);
            String alias = ks.aliases().nextElement();
            PrivateKey pk = (PrivateKey) ks.getKey(alias, PASSWORD);
            // 得到證書鏈
            Certificate[] chain = ks.getCertificateChain(alias);
            //需要進行簽章的pdf
            String path = "D:/demo.pdf";
            // 封裝簽章資訊
            SignatureInfo signInfo = new SignatureInfo();
            signInfo.setReason("理由");
            signInfo.setLocation("位置");
            signInfo.setPk(pk);
            signInfo.setChain(chain);
            signInfo.setCertificationLevel(PdfSignatureAppearance.NOT_CERTIFIED);
            signInfo.setDigestAlgorithm(DigestAlgorithms.SHA1);
            signInfo.setFieldName("demo");
            // 簽章圖片
            signInfo.setImagePath("d:/sign.jpg");
            signInfo.setRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC);
            signInfo.setRectllx(100);  // 值越大,代表向x軸座標平移 縮小 (反之,值越小,印章會放大)
            signInfo.setRectlly(200);  // 值越大,代表向y軸座標向上平移(大小不變)
            signInfo.setRecturx(400);  // 值越大   代表向x軸座標向右平移  (大小不變)
            signInfo.setRectury(100);  // 值越大,代表向y軸座標向上平移(大小不變)
            //簽章後的pdf路徑
            app.sign(path, "D:/demo3.pdf", signInfo);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

為了方便各位理解,這裡我將d盤所用到的檔案在這裡截圖展示。

這裡寫圖片描述

  • 然後我們開啟demo3.pdf,就可以看到pdf中已經有一個印章了,如果各位對上面程式碼中的一些引數不是很瞭解,歡迎留言交流。

這裡寫圖片描述

【itext學習之路】系列教程

【itext學習之路】—–(第一篇)建立一個簡單的pdf文件
【itext學習之路】—–(第二篇)設定pdf的一些常用屬性
【itext學習之路】—–(第三篇)對pdf文件進行加密和許可權設定
【itext學習之路】—–(第四篇)給pdf增加文字水印和圖片水印
【itext學習之路】—–(第五篇)對pdf進行蓋章/簽章/數字簽名