Java对PDF进行电子签章CA签名认证

什么是CA?
CA是认证中心的英文Certification Authority的缩写。它为电子商务环境中各个实体颁发数字证书,以证明各实体身份的真实性,并负责在交易中检验和管理证书;它是电子商务和网上银行交易的权威性、可信赖性及公正性的第三方机构。

前期准备
首先,你的电脑上需要安装jdk并且安装环境变量,这些网上一搜一大堆就不叙述了

 

 

然后打开黑窗口(cmd) 要用管理员的身份打开!

 

 

输入下面内容

keytool -genkey -alias lianyi -keyalg RSA -validity 30 -keystore android.keystore

简单说明下

-alias 证书别名
-keyalg 算法,有两种:RSA 和 CipherSuite RSA
-validity 证书有效期,我这里是30天
-keystore 证书的名称以及路径(方便演示,我这里放在桌面)
输入口令的时候,出于安全考虑是不显示的。直接输入密码再次确认就行。

内容其实可以随便写,但是得记住!

 

 

回车完之后在哪里找到呢?
打开C盘 -> 用户 -> [用户名] 就可以找到刚才生成的证书了。
或者再jdk的bin里面

注意:如果你执行不了或者报错很正常,因为我也是这样的,各种错误不断,反正报什么报错就在网上搜就完事了,总有解决的办法

经过以上步骤就可以获得keystore 证书文件了,一会我们代码用

梳理下我们需要用到的东西
1 一个pdf文件
2 一个电子签证图片
3 证书文件

 

 

ok,开始写代码!

jar包准备:

bcpkix-jdk15on-1.70.jar

bcprov-jdk15on-1.70.jar

iTextAsian.jar

itextpdf-5.5.13.3.jar

代码实现
首先你得有一个可以运行的maven项目

依赖如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>keystore-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>keystore-demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.4.0</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.itextpdf/itextpdf -->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.5.10</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.itextpdf/itext-asian -->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext-asian</artifactId>
            <version>5.2.0</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.bouncycastle/bcpkix-jdk15on -->
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcpkix-jdk15on</artifactId>
            <version>1.60</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on -->
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk15on</artifactId>
            <version>1.60</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

签署工具类

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 java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.cert.Certificate;

public class KeystoreUtils {

    /**
     *
     * @param src 需要签章的pdf文件路径
     * @param dest 签完章的pdf文件路径
     * @param chain 证书链
     * @param img 印章图片
     * @param pk 签名私钥
     * @param digestAlgorithm 摘要算法名称,例如SHA-1
     * @param provider  密钥算法提供者,可以为null
     * @param subfilter 数字签名格式,itext有2种
     * @param reason 签名的原因,显示在pdf签名属性中
     * @param location 签名的地点,显示在pdf签名属性中
     * @throws GeneralSecurityException
     * @throws IOException
     * @throws DocumentException
     */
    public void sign(String src, String dest,String img, Certificate[] chain, PrivateKey pk, String digestAlgorithm, String provider,
                     MakeSignature.CryptoStandard subfilter, String reason, String location) throws GeneralSecurityException, IOException, DocumentException {

        PdfReader pdfReader = new PdfReader(src);
        FileOutputStream fileOutputStream = new FileOutputStream(dest);
        
        /**
         * 1 参数依次为:文件名、文件输入流、文件版本号、临时文件、是否可以追加签名
         *  1.1 false的话,pdf文件只允许被签名一次,多次签名,最后一次有效
         *  1.2 true的话,pdf可以被追加签名,验签工具可以识别出每次签名之后文档是否被修改
         */
        PdfStamper stamper = PdfStamper.createSignature(pdfReader, fileOutputStream, '\0', null, false);
        // 获取数字签章属性对象,设定数字签章的属性
        PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
        appearance.setReason(reason);
        appearance.setLocation(location);
        /**
         * 1 三个参数依次为:设置签名的位置、页码、签名域名称,多次追加签名的时候,签名域名称不能一样
         *  1.1 签名的位置四个参数:印章左下角的X、Y轴坐标,印章右上角的X、Y轴坐标,
         *         这个位置是相对于PDF页面的位置坐标,即该坐标距PDF当前页左下角的坐标
         */
        appearance.setVisibleSignature(new Rectangle(100, 100, 200, 200), 1, "sign");
        
        /**
         * 用于盖章的印章图片,引包的时候要引入itext包的image
         */
        Image image = Image.getInstance(img);
        appearance.setSignatureGraphic(image);
        
        /**
         * 设置认证等级,共4种,分别为:
         *  NOT_CERTIFIED、CERTIFIED_NO_CHANGES_ALLOWED、
         *  CERTIFIED_FORM_FILLING 和 CERTIFIED_FORM_FILLING_AND_ANNOTATIONS
         *  
         * 需要用哪一种根据业务流程自行选择
         */
        appearance.setCertificationLevel(PdfSignatureAppearance.NOT_CERTIFIED);
        
        /**
         * 印章的渲染方式,同样有4种:
         *  DESCRIPTION、NAME_AND_DESCRIPTION,
         *  GRAPHIC_AND_DESCRIPTION,GRAPHIC;
         * 这里选择只显示印章
         */
        appearance.setRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC);

        /**
         * 算法主要为:RSA、DSA、ECDSA
         * 摘要算法,这里的itext提供了2个用于签名的接口,可以自己实现
         */
        ExternalDigest digest = new BouncyCastleDigest();
        /**
         * 签名算法,参数依次为:证书秘钥、摘要算法名称,例如MD5 | SHA-1 | SHA-2.... 以及 提供者
         */
        ExternalSignature signature = new PrivateKeySignature(pk, digestAlgorithm, null);
        /**
         * 最重要的来了,调用itext签名方法完成pdf签章
         */
        MakeSignature.signDetached(appearance, digest, signature, chain, null, null, null, 0, subfilter);
    }
}

OK 到这里我们的签署就完成了,下面只需要去提供参数调用该工具类就行

测试类代码

import com.example.utils.KeystoreUtils;
import com.itextpdf.text.pdf.security.DigestAlgorithms;
import com.itextpdf.text.pdf.security.MakeSignature;
import org.springframework.boot.test.context.SpringBootTest;

import javax.swing.*;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.Certificate;

@SpringBootTest
class KeystoreDemoApplicationTests {

    public static final String KEYSTORE = "D:\\file\\debug.keystore";
    // 之前生成的keystory密码
    public static final char[] PASSWORD = "123456".toCharArray();
    // 需要签名的PDF路径
    public static final String SRC = "D:\\file\\abc.pdf";
    // 完成签名的PDF路径
    public static final String OUTPUT_SRC = "D:\\file\\finishAC.pdf";
    public static final String IMG = "D:\\file\\1.jpg";


    public static void main(String[] args) {
        try {
            //读取keystore ,获得私钥和证书链
            KeyStore keyStore = KeyStore.getInstance("JKS");
            keyStore.load(new FileInputStream(KEYSTORE), PASSWORD);
            String alias = (String)keyStore.aliases().nextElement();
            PrivateKey PrivateKey = (PrivateKey) keyStore.getKey(alias, PASSWORD);
            Certificate[] chain = keyStore.getCertificateChain(alias);

            KeystoreUtils keystoreUtils = new KeystoreUtils();
            keystoreUtils.sign(SRC, String.format(OUTPUT_SRC, 3),IMG, chain, PrivateKey, DigestAlgorithms.SHA1, null, MakeSignature.CryptoStandard.CMS, "Test", "Ghent");
        } catch (Exception e) {
            JOptionPane.showMessageDialog(null, e.getMessage());
            e.printStackTrace();
        }
    }
}

验收结果

打开我们的文件,可以发现已经有了签名

 

 点击签名查看信息

 

 

 

 

这样,我们就完成了电子签名。

签名的大小以及位置,主要在这里控制

appearance.setVisibleSignature(new Rectangle(200, 200, 300, 300), 1, "sign1");

转载:https://blog.csdn.net/weixin_46713508/article/details/125660108

posted @ 2022-12-13 10:00  Ferocious  阅读(3105)  评论(0编辑  收藏  举报