使用Java对PDF进行电子签章

news/2024/11/7 10:48:37/

使用Java对PDF进行电子签章

  • 开始之前
  • 前期准备
  • 开始
    • 生成keystore证书
    • 来张材料全家福
    • 编码
      • 项目结构
      • 签署工具类

开始之前

公司近期做的项目用到了电子签章(给PDF盖章签名),这过程真是曲折。恰逢现在时间比较空闲(有时间摸鱼)。我把签章的过程给记录下来

前期准备

1. 需要签名的PDF (废话,没有PDF签什么名啊~)
2. 给PDF签名的印章图片 (貌似也是废话)
3. keystore 证书文件 (先认识认识,教程接下来会说怎么生成)

开始

生成keystore证书

  • 安装有JDK的机器,并配置系统变量 (安装教程很多,自行百度)
    墨染涟漪
  • 搜索cmd选择管理员身份打开!(管理员身份运行!管理员身份运行!管理员身份运行!
    墨染涟漪
  • 输入以下信息
keytool -genkey -alias lianyi -keyalg RSA -validity 30 -keystore android.keystore

说明:

  • -alias 证书别名

  • -keyalg 算法,有两种:RSA 和 CipherSuite RSA

  • -validity 证书有效期,我这里是30天

  • -keystore 证书的名称以及路径(方便演示,我这里放在桌面)

    输入口令的时候,出于安全考虑是不显示的。直接输入密码再次确认就行。
    墨染涟漪

  • 然后输入一些信息,按照提示输入就行。输入密钥口令的时候如果和上面密钥库口令相同可以直接回车,我这里使用相同的,直接回车。
    墨染涟漪

  • 回车完之后在哪里找到呢?
    打开C盘 -> 用户 -> [用户名] 就可以找到刚才生成的证书了。
    墨染涟漪

来张材料全家福

在这里插入图片描述

至此,需要的资料就准备完毕了~

接下来…废话少说,直接上号墨染涟漪

编码

创建项目:
墨染涟漪
输入工程名,组织名,不说了
墨染涟漪
什么都不选,等下自己加
墨染涟漪
瞄一眼pom文件里加的坐标,其实都可以从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>

项目结构

-放一张结构图,防止找不到在哪里
墨染涟漪

签署工具类

  • 接下来我们新建一个签章文件KeystoreUtils.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 java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.cert.Certificate;/**** @author 墨* @create 2021-04-28 17:33*/
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 到这里我们的签署就完成了,下面只需要去提供参数调用该工具类就行。那我们直接开搞

  • 在测试类KeystoreDemoApplicationTests给出以下代码
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:\\android.keystore";// 之前生成的keystory密码public static final char[] PASSWORD = "123456".toCharArray();// 需要签名的PDF路径public static final String SRC = "D:\\abc.pdf";// 完成签名的PDF路径public static final String OUTPUT_SRC = "D:\\abc-sign.pdf";public static final String IMG = "D:\\seal01.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();}}
}

直接运行看下效果
在这里插入图片描述

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

我们再修改下一下KeystoreUtils中印章位置

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

再运行查看
在这里插入图片描述
看到位置已经改变~
做是做完了,但是还记得那两行摘要算法和签名算法吗?回顾一下

ExternalDigest digest = new BouncyCastleDigest();ExternalSignature signature = new PrivateKeySignature(pk, digestAlgorithm, null);

我们去康康摘要算法是怎么实现的

public class BouncyCastleDigest implements ExternalDigest {public BouncyCastleDigest() {}public MessageDigest getMessageDigest(String hashAlgorithm) throws GeneralSecurityException {String oid = DigestAlgorithms.getAllowedDigests(hashAlgorithm);if (oid == null) {throw new NoSuchAlgorithmException(hashAlgorithm);} else if (oid.equals("1.2.840.113549.2.2")) {return new Digest();} else if (oid.equals("1.2.840.113549.2.5")) {return new org.bouncycastle.jcajce.provider.digest.MD5.Digest();} else if (oid.equals("1.3.14.3.2.26")) {return new org.bouncycastle.jcajce.provider.digest.SHA1.Digest();} else if (oid.equals("2.16.840.1.101.3.4.2.4")) {return new org.bouncycastle.jcajce.provider.digest.SHA224.Digest();} else if (oid.equals("2.16.840.1.101.3.4.2.1")) {return new org.bouncycastle.jcajce.provider.digest.SHA256.Digest();} else if (oid.equals("2.16.840.1.101.3.4.2.2")) {return new org.bouncycastle.jcajce.provider.digest.SHA384.Digest();} else if (oid.equals("2.16.840.1.101.3.4.2.3")) {return new org.bouncycastle.jcajce.provider.digest.SHA512.Digest();} else if (oid.equals("1.3.36.3.2.2")) {return new org.bouncycastle.jcajce.provider.digest.RIPEMD128.Digest();} else if (oid.equals("1.3.36.3.2.1")) {return new org.bouncycastle.jcajce.provider.digest.RIPEMD160.Digest();} else if (oid.equals("1.3.36.3.2.3")) {return new org.bouncycastle.jcajce.provider.digest.RIPEMD256.Digest();} else if (oid.equals("1.2.643.2.2.9")) {return new org.bouncycastle.jcajce.provider.digest.GOST3411.Digest();} else {throw new NoSuchAlgorithmException(hashAlgorithm);}}
}

哦~ 这一顿操作下来原来是要我们给算法名啊。根据不同的算法获取对应的签名摘要。当然我们也可以自己去实现,签名算法同样也可以由我们自己实现(敲黑板,我要留作业了)

你学废了吗?

创作不易,请点个赞嘛~


http://www.ppmy.cn/news/356737.html

相关文章

1024程序员节|历经一个月总结使用java实现pdf文件的电子签字+盖章+防伪二维码+水印+PDF文件加密的全套解决方案

&#x1f345;程序员小王的博客&#xff1a;程序员小王的博客 &#x1f345;CSDN地址&#xff1a;程序员小王java &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; &#x1f345; 如有编辑错误联系作者&#xff0c;如果有比较好的文章欢迎分享给我&#xff0c…

怎样在PDF文件上添加印章

1、首先将需要添加制公司的印章的文件转换成.pdf文件 2、从Adobe 官网上下载Adobe Acrobat XI Pro 这款软件并安装到我们的电脑上&#xff0c;安装完后打开软件&#xff0c;界面如下&#xff1a; 3、准备好我们要添加水印的pdf文档&#xff0c;用Adobe Acrobat XI打开&#xf…

Word盖章和PDF盖章

一、电子签章的作用 对文档进行数字签名与签署纸质文档的原因大致相同&#xff0c;数字签名通过使用计算机加密来验证 &#xff08;身份验证:验证人员和产品所声明的身份是否属实的过程。例如&#xff0c;通过验证用于签名代码的数字签名来确认软件发行商的代码来源和完整性。…

css移动端

目录 谷歌模拟器 屏幕分辨率 视口 二倍图 适配方案 rem 简介 问题 媒体查询 移动端 设备宽度不同&#xff0c;HTML标签字号设置多少合适 flexible.js rem-移动端适配 less 注释 运算 嵌套 变量 导入 导出 禁止导出 谷歌模拟器 模拟移动设备&#xff0c;方…

诚迈科技智能汽车软件产业峰会落幕,智达诚远峰昇操作系统FusionOS发布!

4月6日&#xff0c;由诚迈科技、智达诚远共同主办&#xff0c;苏州工业园区投资促进委员会协办&#xff0c;苏州工业园区智能网联产业促进会大力支持的中国&#xff08;苏州&#xff09;智能汽车软件产业峰会在苏州盛大举行。本次活动以“创新融合 聚力赋能”为主题&#xff0c…

Antv/L7中使用高德地图插件

L7可以使用高德地图作为底图&#xff0c;也可以使用高德地图提供的插件&#xff0c;如工具栏&#xff0c;缩放条等。 官方文档中的引入方式如下const sc new Scene({id: xxxx-map,logoVisible: false,map: new GaodeMap({style: default,pitch: 0,zoom: 13.056,plugin: [AMap…

高德地图——货车导航

高德地图——货车导航 插件&#xff1a;pluginAMap.TruckDriving 第一种方法&#xff1a;使用坐标&#xff0c;比驾驶和骑行多了city和size属性&#xff0c;且需要放入的是json数据 new AMap.TruckDriving({map:map,panel:panel,city:beijing,//城市size:1 //大小}).search(…

ECE8.1认证之路

ECE8.1认证之路 现在考试基本不需要发邮件说明要用中国 身份证考试和用deepl翻译软件&#xff0c;如果不放心也可以问&#xff0c;官方也会回复 1、购买考试资格(需要vxn) 点击 My Account 这个时候会让你登录&#xff0c;我用的是微软的账号登录 然后会让你填入一些基本信…