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