【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官方英文API+中文使用说明》

【itext学习之路】系列教程

【itext学习之路】-----(第一篇)创建一个简单的pdf文档
【itext学习之路】-----(第二篇)设置pdf的一些常用属性
【itext学习之路】-----(第三篇)对pdf文档进行加密和权限设置
【itext学习之路】-----(第四篇)给pdf增加文本水印和图片水印
【itext学习之路】-----(第五篇)对pdf进行盖章/签章/数字签名
【itext学习之路】-----(第六篇)将html转成pdf(解决中文不显示)

欢迎关注本人个人公众号,交流更多技术信息

在这里插入图片描述

  • 15
    点赞
  • 91
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 25
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tomatocc

赏杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值