目录
1. 电子公章的制作
做章网站:http://seal.biaozhiku.com/
-
我们选择圆形印章
- 然后输入公司名,输入章名输入编码然后点击395生成,最后点击保存图片,我们的个人专业章就实现了
2. java工具keytool生成p12数字证书文件
Keytool是用于管理**和证书的工具,位于%JAVA_HOME%/bin目录。 使用JDK的keytool工具
- keytool在jdk的bin目录下
2. 打开keytool所在的bin目录,然后在上面的路径显示框中输入CMD,然后回车,即可在当前文件夹下打开命令提示符,并且路径是当前文件夹。
- 生成数字文件,在命令行输入
-
keytool -genkeypair -alias whj -keypass 111111 -storepass 111111 -dname “C=CN,ST=SD,L=QD,O=haier,OU=dev,CN=haier.com” -keyalg RSA -keysize 2048 -validity 3650 -keystore D:\keystore\server.keystore
参数解释:
storepass keystore 文件存储密码
keypass 私钥加解密密码
alias 实体别名(包括证书私钥)
dname 证书个人信息
keyalt 采用公钥算法,默认是DSA keysize **长度(DSA算法对应的默认算法是sha1withDSA,不支持2048长度,此时需指定RSA)
validity 有效期
keystore 指定keystore文件
- 转换为p12格式
在命令行输入
keytool -importkeystore -srckeystore D:\keystore\server.keystore -destkeystore D:\keystore\whj.p12 -srcalias whj -destalias serverkey -srcstoretype jks -deststoretype pkcs12 -srcstorepass 111111 -deststorepass 111111 -noprompt
- 生成的最终文件
3. pom依赖
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.11</version>
</dependency>
<!--itext7 pom-->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext7-core</artifactId>
<version>7.2.0</version>
<type>pom</type>
</dependency>
4. 实体类
package com.example.demo.itext.model;
import com.itextpdf.signatures.PdfSignatureAppearance;
import java.security.PrivateKey;
import java.security.cert.Certificate;
public class SignatureInfo {
private String reason; //签名的原因,显示在pdf签名属性中
/**
* 将证书文件放入指定路径,并读取keystore ,获得私钥和证书链
*/
private String pkPath;
/**
* KeyStore类型
*/
private String keyStoreType;
/**
* 原PDF文件路径
*/
private String src;
/**
* 数字签章后的文件路径
*/
private String target;
private String location;//签名的地点,显示在pdf签名属性中
private String digestAlgorithm;//摘要算法名称,例如SHA-1
private String imagePath;//图章路径
private String fieldName;//表单域名称
private Certificate[] chain;//证书链
private PrivateKey pk;//签名私钥
private int certificationLevel = 0; //批准签章
private PdfSignatureAppearance.RenderingMode renderingMode;//表现形式:仅描述,仅图片,图片和描述,签章者和描述
//图章属性
private float rectllx;//图章左下角x
private float rectlly;//图章左下角y
private float recturx;//图章右上角x
private float rectury;//图章右上角y
public float getRectllx() {
return rectllx;
}
public void setRectllx(float rectllx) {
this.rectllx = rectllx;
}
public float getRectlly() {
return rectlly;
}
public void setRectlly(float rectlly) {
this.rectlly = rectlly;
}
public float getRecturx() {
return recturx;
}
public void setRecturx(float recturx) {
this.recturx = recturx;
}
public float getRectury() {
return rectury;
}
public void setRectury(float rectury) {
this.rectury = rectury;
}
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 String getDigestAlgorithm() {
return digestAlgorithm;
}
public void setDigestAlgorithm(String digestAlgorithm) {
this.digestAlgorithm = digestAlgorithm;
}
public String getImagePath() {
return imagePath;
}
public void setImagePath(String imagePath) {
this.imagePath = imagePath;
}
public String getFieldName() {
return fieldName;
}
public void setFieldName(String fieldName) {
this.fieldName = fieldName;
}
public Certificate[] getChain() {
return chain;
}
public void setChain(Certificate[] chain) {
this.chain = chain;
}
public PrivateKey getPk() {
return pk;
}
public void setPk(PrivateKey pk) {
this.pk = pk;
}
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 getPkPath() {
return pkPath;
}
public void setPkPath(String pkPath) {
this.pkPath = pkPath;
}
public String getKeyStoreType() {
return keyStoreType;
}
public void setKeyStoreType(String keyStoreType) {
this.keyStoreType = keyStoreType;
}
public String getSrc() {
return src;
}
public void setSrc(String src) {
this.src = src;
}
public String getTarget() {
return target;
}
public void setTarget(String target) {
this.target = target;
}
}
5. 工具类及测试示例
ItextUtil工具类参考:https://mp.csdn.net/mp_blog/creation/editor/128789640
package com.example.demo.itext.util;
import com.example.demo.itext.model.KeyWordBean;
import com.example.demo.itext.model.SignatureInfo;
import com.itextpdf.io.image.ImageData;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.StampingProperties;
import com.itextpdf.signatures.*;
import lombok.SneakyThrows;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.util.ResourceUtils;
import java.io.*;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.util.List;
import java.util.Map;
/**
* 功能描述: pdf数字签章工具类 <br>
*
* @Author: lhp
* @Date: 2023/1/29 15:45
*/
public class PdfStampUtil {
/**
* keystory密码,可以放到配置文件中
*/
public static final char[] PASSWORD = "111111".toCharArray();
/**
* 功能描述: PDF添加数字签章 <br>
*
* @Param: [src:原PDF文件路径, target:数字签章后的文件路径, signatureInfo:参数]
* @Return: void
* @Author: lhp
* @Date: 2023/1/29 15:33
*/
public static void sign(SignatureInfo signatureInfo) {
// 原PDF文件路径
String src = signatureInfo.getSrc();
// 数字签章后的文件输出路径
String target = signatureInfo.getTarget();
InputStream inputStream = null;
FileOutputStream outputStream = null;
ByteArrayOutputStream result = new ByteArrayOutputStream();
try {
inputStream = new FileInputStream(src);
ByteArrayOutputStream tempArrayOutputStream = new ByteArrayOutputStream();
PdfReader reader = new PdfReader(inputStream);
PdfSigner stamper = new PdfSigner(reader, tempArrayOutputStream, new StampingProperties());
stamper.setCertificationLevel(signatureInfo.getCertificationLevel());
// 获取数字签章属性对象
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setReason(signatureInfo.getReason());
appearance.setLocation(signatureInfo.getLocation());
// 读取图章图片
ImageData image = ImageDataFactory.create(signatureInfo.getImagePath());
appearance.setSignatureGraphic(image);
Float rectllx = signatureInfo.getRectllx();
System.out.println("x:" + rectllx);
Float rectlly = signatureInfo.getRectlly();
System.out.println("y:" + rectlly);
Float recturx = signatureInfo.getRecturx();
System.out.println("recturx:" + recturx);
Float rectury = signatureInfo.getRectury();
System.out.println("rectury:" + rectury);
appearance.setPageNumber(1);
appearance.setPageRect(new Rectangle(rectllx, rectlly, recturx, rectury));
// appearance.setPageRect(new Rectangle(rectllx, rectlly, image.getWidth(), image.getHeight()));
// 设置图章的显示方式,如下选择的是只显示图章(还有其他的模式,可以图章和签名描述一同显示)
appearance.setRenderingMode(signatureInfo.getRenderingMode());
// 这里的itext提供了2个用于签名的接口,可以自己实现,后边着重说这个实现
// 摘要算法
IExternalDigest digest = new BouncyCastleDigest();
// 签名算法
Security.addProvider(new BouncyCastleProvider());
// 签名算法
IExternalSignature signature = new PrivateKeySignature(signatureInfo.getPk(), signatureInfo.getDigestAlgorithm(), BouncyCastleProvider.PROVIDER_NAME);
// 调用itext签名方法完成pdf签章 //数字签名格式,CMS,CADE
stamper.signDetached(digest, signature, signatureInfo.getChain(), null, null, null, 0, PdfSigner.CryptoStandard.CADES);
inputStream = new ByteArrayInputStream(
tempArrayOutputStream.toByteArray());
// 定义输入流为生成的输出流内容,以完成多次签章的过程
result = tempArrayOutputStream;
outputStream = new FileOutputStream(target);
outputStream.write(result.toByteArray());
outputStream.flush();
reader.close();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
if (null != outputStream) {
outputStream.close();
}
if (null != inputStream) {
inputStream.close();
}
if (null != result) {
result.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@SneakyThrows
public static void pdfStamp(SignatureInfo signatureInfo) {
String keyStoreType = signatureInfo.getKeyStoreType();
KeyStore ks = KeyStore.getInstance(keyStoreType);
// 将证书文件放入指定路径,并读取keystore ,获得私钥和证书链
ks.load(new FileInputStream(ResourceUtils.getFile("classpath:whj.p12")), PASSWORD);
String alias = ks.aliases().nextElement();
PrivateKey pk = (PrivateKey) ks.getKey(alias, PASSWORD);
// 得到证书链
Certificate[] chain = ks.getCertificateChain(alias);
signatureInfo.setPk(pk);
signatureInfo.setChain(chain);
signatureInfo.setCertificationLevel(1);
signatureInfo.setDigestAlgorithm(DigestAlgorithms.SHA1);
signatureInfo.setRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC);
sign(signatureInfo);
}
public static void main(String[] args) {
SignatureInfo signInfo = new SignatureInfo();
signInfo.setReason("理由");
signInfo.setLocation("位置");
signInfo.setFieldName("demo");
// 签章图片
String imagePath = "F:\\software\\myfile\\南无阿弥陀佛电子印章制作平台.png";
signInfo.setImagePath(imagePath);
// 需要进行签章的pdf
String path = "F:\\software\\myfile\\txt12_加水印_加水印.pdf";
signInfo.setSrc(path);
String target = "F:\\software\\myfile\\txt12-PdfStamp15.pdf";
signInfo.setTarget(target);
String keyStoreType = "PKCS12";
signInfo.setKeyStoreType(keyStoreType);
// 值越大,代表向x轴坐标平移 缩小 (反之,值越小,印章会橫向放大)
// signInfo.setRectllx(100);
// 值越大,代表向y轴坐标向上平移(反之,值越小,印章会纵向放大)
// signInfo.setRectlly(100);
// 值越大 代表向x轴坐标向右平移 (大小不变)
signInfo.setRecturx(100);
// 值越大,代表向y轴坐标向上平移(大小不变)
signInfo.setRectury(100);
Map<Integer, List<KeyWordBean>> listMap = ItextUtil.keyWordLocationMap("负责人签名:", path);
KeyWordBean keyWordBean = listMap.get(1).get(0);
// KeyWordBean keyWordBean = listMap.get(1).get(1);
signInfo.setRectllx(keyWordBean.getX() + 70);
signInfo.setRectlly(keyWordBean.getY() - 50);
pdfStamp(signInfo);
}
}
6. 效果
参考: