通过数字证书对PDF电子文件进行数字签名/盖章

以下代码详细说明如何使用数字证书对PDF电子文件进行数字签名/盖章。PDF文件签署主要传递PDF文件,数字证书信息,签章图片3个信息。代码中需要的文件、数字证书、签章图片可访问开放签电子签章开源系统详细了解系统的实现与效果。也可通过gitee开源社区下载开放签开源电子签章系统,获取所有开源代码。

1、数字签名/盖章类SignService.java

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

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、签署位置信息类

java 复制代码
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 ;
}
相关推荐
途途途途4 分钟前
100个python经典面试题详解(新版)
开发语言·python·最新面试题·python面试题
以卿a9 分钟前
C++ 类和对象(类型转换、static成员)
开发语言·c++·算法
油泼辣子多加10 分钟前
【计算机视觉】图像基本操作
图像处理·人工智能·python·opencv·计算机视觉
O_o38122 分钟前
droppath
人工智能·pytorch·python·深度学习
Muisti36 分钟前
P7184 [CRCI2008-2009] MAJSTOR 多层循环的遍历
开发语言·c++·算法·leetcode
龙马啊38 分钟前
win32com python 操作wps 解决修改 表格触发关闭 其他excel的功能
python·excel·wps
Eric.Lee202143 分钟前
SenseVoice 音频转文字&情绪识别 - python 实现
服务器·人工智能·python·音视频·情绪识别·音频识别
晚渔声1 小时前
【线程】Java多线程代码案例(2)
java·开发语言·多线程
一只小菜鸡1 小时前
python+django自动化平台(一键执行sql) 前端vue-element展示
python·mysql·django·自动化
5-StarrySky1 小时前
Java 线程中的分时模型和抢占模型
java·开发语言