通过数字证书对PDF电子文件进行数字签名/盖章
以下代码详细说明如何使用数字证书对PDF电子文件进行数字签名/盖章。PDF文件签署主要传递PDF文件,数字证书信息,签章图片3个信息。代码中需要的文件、数字证书、签章图片可访问开放签电子签章开源系统详细了解系统的实现与效果。也可通过gitee开源社区下载开放签开源电子签章系统,获取所有开源代码。
1、数字签名/盖章类SignService.java
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.*;
import com.resrun.service.pojo.CertificateProperty;
import com.resrun.service.pojo.RealPositionProperty;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.stereotype.Service;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
/**
 * @Description: 签署业务
 * @Package: com.resrun.service.pdf
 * @ClassName: SignService
 * @copyright 北京资源律动科技有限公司 www.kaifangqian.com
 */
@Service
public class SignService {
    public byte[] signingContract(byte[] pdfFile, byte[] signBadge, CertificateProperty cert,
                                  RealPositionProperty position) throws GeneralSecurityException, IOException, DocumentException {
        System.setProperty("javax.xml.parsers.DocumentBuilderFactory","com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl");
        Security.addProvider(new BouncyCastleProvider());
        //1、解析证书
        // Java 安全属性文件中指定的默认 keystore 类型;如果不存在此类属性,则返回字符串 "jks"。 PKCS12
        KeyStore ks = KeyStore.getInstance(cert.getCertType());
        try {
            char[] chars = cert.getPassword().toCharArray();
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(cert.getCertFile());
            ks.load(byteArrayInputStream, chars);
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 获取keystore中的所有别名
        String alias = (String) ks.aliases().nextElement();
        // 返回:请求的密钥, 入力参数:别名,用于恢复密钥的密码
        PrivateKey pk = (PrivateKey) ks.getKey(alias, cert.getPassword().toCharArray());
        // 证书链(按用户证书在前,根证书授权在后的顺序)
        Certificate[] chain = ks.getCertificateChain(alias);
        byte[] signedFileByte = null ;
        PdfReader reader = null ;
        ByteArrayOutputStream signedFile = null ;
        PdfStamper stamper = null ;
        try {
            //2、读取PDF文件
            reader = new PdfReader(pdfFile);
            signedFile = new ByteArrayOutputStream();
            stamper = PdfStamper.createSignature(reader, signedFile, '\0', null, true);
            //3、给签署属性服务
            PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
            if (signBadge == null || position == null) {
                appearance.setCertificationLevel(certificationLevel);
            } else {
                int pageNum = 0;
                if (inspect) {
                    //如果检查就会抛出检查异常
                    pageNum = position.getPageNum();
                    if (pageNum == 0)
                        throw new IllegalArgumentException("Pdf page number must be greater than one....!!!");
                } else {
                    pageNum = position.getPageNum() <= 0 ? 1 : position.getPageNum();
                }
                appearance.setVisibleSignature(new Rectangle(position.getStartx(), position.getStarty(), position.getEndx(), position.getEndy()), pageNum, null);
                // 添加签章图片
                Image img = Image.getInstance(signBadge);
                appearance.setSignatureGraphic(img);
                appearance.setImageScale(-1);
                appearance.setCertificationLevel(certificationLevel);
                appearance.setRenderingMode(renderingMode);
            }
            appearance.setReason(reason);
            appearance.setLocation(location);
            //4、调用签署  Creating the signature
            ExternalSignature pks = new PrivateKeySignature(pk, hashAlgorithm, BouncyCastleProvider.PROVIDER_NAME);
            ExternalDigest digest = new BouncyCastleDigest();
            MakeSignature.signDetached(appearance, digest, pks, chain, null, ocspClient, tsaClient, 0, cryptoStandard);
            signedFileByte = signedFile.toByteArray();
        } catch (Exception e){
            e.printStackTrace();
        }finally {
            // 关闭流
            if (stamper != null) stamper.close();
            if (signedFile != null) signedFile.close();
            if (reader != null) reader.close();
        }
        return signedFileByte ;
    }
    //是否判断校验不校验PDF页码
    private boolean inspect = true;
    private int certificationLevel = PdfSignatureAppearance.NOT_CERTIFIED;
    private PdfSignatureAppearance.RenderingMode renderingMode = PdfSignatureAppearance.RenderingMode.GRAPHIC;
    private String hashAlgorithm = DigestAlgorithms.SHA256;
    private MakeSignature.CryptoStandard cryptoStandard = MakeSignature.CryptoStandard.CMS;
    private String reason = "防伪造防篡改数字校验"; //原因
    private String location; //位置
    private TSAClient tsaClient; //时间戳服务
    private OcspClient ocspClient;
    public boolean isInspect() {
        return inspect;
    }
    public void setInspect(boolean inspect) {
        this.inspect = inspect;
    }
    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;
    }
    public String getHashAlgorithm() {
        return hashAlgorithm;
    }
    public void setHashAlgorithm(String hashAlgorithm) {
        this.hashAlgorithm = hashAlgorithm;
    }
    public MakeSignature.CryptoStandard getCryptoStandard() {
        return cryptoStandard;
    }
    public void setCryptoStandard(MakeSignature.CryptoStandard cryptoStandard) {
        this.cryptoStandard = cryptoStandard;
    }
    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 TSAClient getTsaClient() {
        return tsaClient;
    }
    public void setTsaClient(TSAClient tsaClient) {
        this.tsaClient = tsaClient;
    }
    public OcspClient getOcspClient() {
        return ocspClient;
    }
    public void setOcspClient(OcspClient ocspClient) {
        this.ocspClient = ocspClient;
    }
}
2、证书文件属性类,主要存储证书信息CertificateProperty.java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
 * @Description: 证书文件属性类
 * @Package: com.resrun.service.pojo
 * @ClassName: CertificateProperty
 * @copyright 北京资源律动科技有限公司
 */
@AllArgsConstructor
@NoArgsConstructor
@Data
public class CertificateProperty implements Serializable {
    private static final long serialVersionUID = -2073805779543816269L;
    private  byte[] certFile;
    /** 证书的类型 比如:PKCS12和jks*/
    private  String certType;
    /** 证书密码 */
    private  String password;
}
3、签署位置信息类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
 * @Description: 经过计算后的文件签署位置属性类
 * @Package: com.resrun.service.pojo
 * @ClassName: PositionProperty
 * @copyright 北京资源律动科技有限公司
 */
@AllArgsConstructor
@NoArgsConstructor
@Data
public class RealPositionProperty implements Serializable {
        private static final long serialVersionUID = 8586984409612483553L;
        /** 签章左下角x坐标 */
        private  float startx;
        /** 签章左下角y坐标*/
        private  float starty;
        /** 签章右上角x坐标*/
        private  float endx;
        /** 签章右上角x坐标*/
        private  float endy;
        private  int pageNum;
        // 填写值,填写专用
        private String value ;
        //对齐方式
        private String align ;
        //字体
        private String fontFamily ;
        //文字大小
        private Integer fontSize ;
}												
											通过数字证书对PDF电子文件进行数字签名/盖章的更多相关文章
- makecert 制作数字证书  给DLL加一个数字签名
		
声明:文章整理自互联网 我仅需要给dll添加(替换)一个签名,所以我只看了第一步和第三步,其余的部分我没有测试,不能保证内容的是否正确. 看了很多关于DLL加签名的教程 大多是错误的 完全无法正常走下 ...
 - C#使用BouncyCastle生成PKCS#12数字证书
		
背景 生成数字证书用于PDF文档数字签名 数字证书需要考虑环境兼容性,如linux.windows 网上资料不全或版本多样 本文章主要介绍了在C#中使用BouncyCastle生成PKCS#12个人信 ...
 - HTTPS 基础知识(密钥、对称加密、非对称加密、数字签名、数字证书)
		
HTTPS 概述 对称加密 非对称加密 非对称加密改良方案 非对称加密 + 对称加密 中间人攻击 数字证书 数字签名 HTTPS 工作原理 HTTPS 概述 HTTPS(全称:Hyper Text T ...
 - 通俗理解数字签名,数字证书和https
		
最近在开发关于PDF合同文档电子签章的功能,大概意思就是在一份PDF合同上签名,盖章,使其具有法律效应.签章有法律效应必须满足两个条件: 能够证明签名,盖章者是谁,无法抵赖 PDF合同在签章后不能被更 ...
 - 转!!通俗理解数字加密,数字签名,数字证书和https
		
原博文地址:https://www.jianshu.com/p/4932cb1499bf 前言 最近在开发关于PDF合同文档电子签章的功能,大概意思就是在一份PDF合同上签名,盖章,使其具有法律效应. ...
 - 通俗理解数字签名,ssl数字证书和https
		
前言 最近在开发关于PDF合同文档电子签章的功能,大概意思就是在一份PDF合同上签名,盖章,使其具有法律效应.签章有法律效应必须满足两个条件: 能够证明签名,盖章者是谁,无法抵赖 PDF合同在签章后不 ...
 - 和安全有关的那些事(非对称加密、数字摘要、数字签名、数字证书、SSL、HTTPS及其他)
		
转自http://blog.csdn.net/bluishglc/article/details/7585965 对于一般的开发人员来说,很少需要对安全领域内的基础技术进行深入的研究,但是鉴于日常系统 ...
 - 网络通信分享(一):数字签名,数字证书,https通信,数据加密
		
加密算法: 一:对称加密算法 在对称加密算法中,加密使用的密钥和解密使用的密钥是相同的.也就是说,加密和解密都是使用的同一个密钥.因此对称加密算法要保证安全性的话,密钥要做好保密,只能让使用的人知道, ...
 - 转:  https 单向双向认证说明_数字证书, 数字签名, SSL(TLS) , SASL
		
转自: http://www.cnblogs.com/mailingfeng/archive/2012/07/18/2597392.html 因为项目中要用到TLS + SASL 来做安全认证层. 所 ...
 - RSA 非对称加密 数字签名 数字证书
		
什么是RSA加密算法 RSA加密算法是一种非对称加密算法,算法的数学基础是极大数分解难题. RSA加密算法的强度也就是极大数分解的难度,目前700多位(二进制)的数字已经可以破解,1024位认为是比较 ...
 
随机推荐
- MPI转以太网模块连接300PLC与DCS modbus通信
			
MPI转以太网模块连接300PLC与DCS modbus通信 由300PLC通过MPI转以太网Plus模块作为modbus从站与DCS主站通信实现MPI转RTU与DCS通信 打开兴达易控提供的MPI转 ...
 - [HNCTF 2022 WEEK2]e@sy_flower
			
花指令分析 如果没接触过花指令,先看这个博客,大致了解一下花指令 https://www.cnblogs.com/Here-is-SG/p/15802040.html 点击此处下载附件 查壳 32位, ...
 - WPF 中引入依赖注入(.NET 通用主机)
			
WPF 中引入依赖注入(.NET 通用主机) 在网上看到的文章都是通过 App.cs 中修改配置进行的,这样侵入性很高而且服务主机是通过 App 启动时加载的而不是服务主机加载的 App 有一点违反原 ...
 - ABC319 A-E 题解
			
A 用 map <string, int> 将名字对应的值存下来即可. 赛时代码 B 按照题意暴力模拟,注意细节. 赛时代码 C 答辩题,卡了我半个小时. 枚举 \(1\sim 9\) 的 ...
 - 「CSP-2023」我曾璀璨星空,星月相伴,致远方,致过往。
			
Day -1 像往常一样去上学.虽然身在学校但感觉心还在比赛上.在一个上午课间准备去上厕所时遇见了信息老师.她在教我们班信息之前我的一些奖状的指导教师就是写的她,之前就认识了,每次碰到她都会朝我笑 ...
 - Python操作Word水印:添加文字或图片水印
			
在Word文档中,可以添加半透明的图形或文字作为水印,以保护文档的原创性,防止未经授权的复制或使用.除了提供安全功能外,水印还可以展示文档创作者的信息.附加的文档信息,或者仅用于文档的装饰.本文将介绍 ...
 - k8s-单节点升级为集群(高可用)
			
单master节点升级为高可用集群 对于生产环境来说,单节点master风险太大了. 非常有必要做一个高可用的集群,这里的高可用主要是针对控制面板来说的,比如 kube-apiserver.etcd. ...
 - python3使用sqlite3构建本地持久化缓存
			
环境:Windows 10_x64 python版本:3.9.2 sqlite3版本:3.34.0 日常python开发中会遇到数据持久化的问题,今天记录下如何使用sqlite3进行数据持久化,并提供 ...
 - springboot在线人数统计
			
在线人数统计 笔者做了一个网站,需要统计在线人数. 在线有两种: 一.如果是后台系统如果登录算在线,退出的时候或者cookie.token失效的时候就算下线 二.如果是网站前台,访问的时候就算在线 今 ...
 - 玩转开源 |Hugo 的使用实践
			
Hugo 是一个能够以出色速度构建静态网页的工具,它为我们提供了极具灵活性的平台,可以塑造成符合个人需求的网页.在上一篇博文中已经介绍了 Hugo 的基本搭建步骤,那如何使用 Hugo 搭建符合自己需 ...