最近在研究Itext對pdf進行簽章,記錄一下。java
可參照:http://developers.itextpdf.com/examples/security-itext5/digital-signatures-white-paper中的例子git
一。準備相關文件:算法
1.背景色爲空的印章圖片app
2.擴展名爲.p12的證書(可自行百度keytool 生成證書 pkcs12)maven
3.一個使用acrobat處理過的pdf模板(使用數字簽名表單)工具
二。引入相關maven依賴:字體
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.11</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.49</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.49</version>
</dependency>
三。編寫相關代碼,spa
1.編寫簽章類,收集簽章相關信息code
package com.example.itext.utils; import com.itextpdf.text.pdf.PdfSignatureAppearance; import com.itextpdf.text.pdf.security.MakeSignature; import lombok.Data; import java.security.PrivateKey; import java.security.cert.Certificate; @Data public class SignatureInfo { private String reason; //理由 private String location;//位置 private String digestAlgorithm;//摘要類型 private String imagePath;//圖章路徑 private String fieldName;//表單域名稱 private Certificate[] chain;//證書鏈 private PrivateKey pk;//私鑰 private int certificationLevel = 0; //批准簽章 private PdfSignatureAppearance.RenderingMode renderingMode;//表現形式:僅描述,僅圖片,圖片和描述,簽章者和描述 private MakeSignature.CryptoStandard subfilter;//支持標準,CMS, }CADES
2.編寫工具類,紅色字體爲完成簽章的主體邏輯,以ByteArrayOutputStream充當輸出流是爲了完成屢次簽章,期間不會生成臨時文件。MakeSignature.signDetached()方法每調用一次,必須從新獲取對象
PdfSignatureAppearance對象,由於分析源碼方法可知,該方法調用時,appearance對象的preClosed參數會被置爲false。
package com.example.itext.utils; import com.itextpdf.text.DocumentException; import com.itextpdf.text.Image; 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 */
public void sign(String src, String target, SignatureInfo... signatureInfos){ InputStream inputStream = null; FileOutputStream outputStream = null; ByteArrayOutputStream result = new ByteArrayOutputStream(); try { inputStream = new FileInputStream(src); for (SignatureInfo signatureInfo : signatureInfos) { ByteArrayOutputStream tempArrayOutputStream = new ByteArrayOutputStream(); PdfReader reader = new PdfReader(inputStream); //建立簽章工具PdfStamper ,最後一個boolean參數是否容許被追加簽名 PdfStamper stamper = PdfStamper.createSignature(reader, tempArrayOutputStream, '\0', null, true); // 獲取數字簽章屬性對象 PdfSignatureAppearance appearance = stamper.getSignatureAppearance(); appearance.setReason(signatureInfo.getReason()); appearance.setLocation(signatureInfo.getLocation()); //設置簽名的簽名域名稱,屢次追加簽名的時候,簽名預名稱不能同樣,圖片大小受表單域大小影響(太小致使壓縮) appearance.setVisibleSignature(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簽章 MakeSignature.signDetached(appearance, digest, signature, signatureInfo.getChain(), null, null, null, 0, signatureInfo.getSubfilter()); //定義輸入流爲生成的輸出流內容,以完成屢次簽章的過程
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 = app.getClass().getResource("/template/zhengshu.p12").getPath(); 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); String src = app.getClass().getResource("/template/check.pdf").getPath();
//封裝簽章信息 SignatureInfo info = new SignatureInfo(); info.setReason("理由"); info.setLocation("位置"); info.setPk(pk); info.setChain(chain); info.setCertificationLevel(PdfSignatureAppearance.NOT_CERTIFIED); info.setDigestAlgorithm(DigestAlgorithms.SHA1); info.setFieldName("sig1"); info.setImagePath(app.getClass().getResource("/template/yinzhang.png").getPath()); info.setRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC); SignatureInfo info1 = new SignatureInfo(); info1.setReason("理由1"); info1.setLocation("位置1"); info1.setPk(pk); info1.setChain(chain); info1.setCertificationLevel(PdfSignatureAppearance.NOT_CERTIFIED); info1.setDigestAlgorithm(DigestAlgorithms.SHA1); info1.setFieldName("sig2"); info1.setImagePath(app.getClass().getResource("/template/yinzhang.png").getPath()); info1.setRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC); app.sign(src, "D://sign.pdf", info,info1);
} catch (Exception e) { // TODO Auto-generated catch block
e.printStackTrace(); } } }
至此,電子簽章的代碼整理完畢。