用Java驗(yàn)證pdf文件的電子章簽名
pom.xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.yalong</groupId>
<artifactId>verifyPdf</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<lombok.version>1.18.10</lombok.version>
</properties>
<dependencies>
<!-- <dependency>-->
<!-- <groupId> e-iceblue </groupId>-->
<!-- <artifactId>spire.pdf</artifactId>-->
<!-- <version>3.4.2</version>-->
<!-- </dependency>-->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>e-iceblue</groupId>
<artifactId>spire.pdf.free</artifactId>
<version>2.6.3</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.0.1</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>com.e-iceblue</id>
<url>http://repo.e-iceblue.cn/repository/maven-public/</url>
</repository>
</repositories>
</project>
VerifySignature.java
import com.spire.pdf.PdfDocument;
import com.spire.pdf.security.PdfCertificate;
import com.spire.pdf.security.PdfSignature;
import com.spire.pdf.widget.PdfFormFieldWidgetCollection;
import com.spire.pdf.widget.PdfFormWidget;
import com.spire.pdf.widget.PdfSignatureFieldWidget;
import lombok.Data;
import lombok.ToString;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.*;
import java.awt.*;
@Data
@ToString
class ExcelDataVO implements Serializable {
private String fileName;
private String signDate;
private String validBefore;
private String validAfter;
private String subject;
private String serialNumber;
private Boolean isEffective = false;
}
class ExcelWriter {
//表頭
private static final List<String> CELL_HEADS;
static {
// 類裝載時(shí)就載入指定好的表頭信息,如有需要,可以考慮做成動(dòng)態(tài)生成的表頭
CELL_HEADS = new ArrayList<>();
CELL_HEADS.add("文件名");
CELL_HEADS.add("簽名時(shí)間");
CELL_HEADS.add("有效期");
CELL_HEADS.add("有效期");
CELL_HEADS.add("簽名機(jī)構(gòu)");
CELL_HEADS.add("序列號(hào)");
CELL_HEADS.add("是否通過驗(yàn)簽");
}
/**
* 生成Excel并寫入數(shù)據(jù)信息
*
* @param dataList 數(shù)據(jù)列表
* @return 寫入數(shù)據(jù)后的工作簿對(duì)象
*/
public static Workbook exportData(List<ExcelDataVO> dataList) {
// 生成xlsx的Excel
Workbook workbook = new SXSSFWorkbook();
// 如需生成xls的Excel,請(qǐng)使用下面的工作簿對(duì)象,注意后續(xù)輸出時(shí)文件后綴名也需更改為xls
//Workbook workbook = new HSSFWorkbook();
// 生成Sheet表,寫入第一行的表頭
Sheet sheet = buildDataSheet(workbook);
//構(gòu)建每行的數(shù)據(jù)內(nèi)容
int rowNum = 1;
for (ExcelDataVO data : dataList) {
if (data == null) {
continue;
}
//輸出行數(shù)據(jù)
Row row = sheet.createRow(rowNum++);
convertDataToRow(workbook, data, row);
}
return workbook;
}
/**
* 生成sheet表,并寫入第一行數(shù)據(jù)(表頭)
*
* @param workbook 工作簿對(duì)象
* @return 已經(jīng)寫入表頭的Sheet
*/
private static Sheet buildDataSheet(Workbook workbook) {
Sheet sheet = workbook.createSheet();
// 設(shè)置表頭寬度
for (int i = 0; i < CELL_HEADS.size(); i++) {
sheet.setColumnWidth(i, 4000);
}
// 設(shè)置默認(rèn)行高
sheet.setDefaultRowHeight((short) 400);
// 構(gòu)建頭單元格樣式
CellStyle cellStyle = buildHeadCellStyle(sheet.getWorkbook());
// 寫入第一行各列的數(shù)據(jù)
Row head = sheet.createRow(0);
for (int i = 0; i < CELL_HEADS.size(); i++) {
Cell cell = head.createCell(i);
cell.setCellValue(CELL_HEADS.get(i));
cell.setCellStyle(cellStyle);
}
return sheet;
}
/**
* 設(shè)置第一行表頭的樣式
*
* @param workbook 工作簿對(duì)象
* @return 單元格樣式對(duì)象
*/
private static CellStyle buildHeadCellStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
//對(duì)齊方式設(shè)置
style.setAlignment(HorizontalAlignment.CENTER);
//邊框顏色和寬度設(shè)置
style.setBorderBottom(BorderStyle.THIN);
style.setBottomBorderColor(IndexedColors.BLACK.getIndex()); // 下邊框
style.setBorderLeft(BorderStyle.THIN);
style.setLeftBorderColor(IndexedColors.BLACK.getIndex()); // 左邊框
style.setBorderRight(BorderStyle.THIN);
style.setRightBorderColor(IndexedColors.BLACK.getIndex()); // 右邊框
style.setBorderTop(BorderStyle.THIN);
style.setTopBorderColor(IndexedColors.BLACK.getIndex()); // 上邊框
//設(shè)置背景顏色
style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
//粗體字設(shè)置
Font font = workbook.createFont();
font.setBold(true);
style.setFont(font);
return style;
}
/**
* 將數(shù)據(jù)轉(zhuǎn)換成行
*
* @param data 源數(shù)據(jù)
* @param row 行對(duì)象
*/
private static void convertDataToRow(Workbook workbook, ExcelDataVO data, Row row) {
int cellNum = 0;
Cell cell;
//對(duì)特殊數(shù)值設(shè)置顏色
CellStyle cellStyle = workbook.createCellStyle();
//字體設(shè)置
Font font = workbook.createFont();
font.setBold(true);
font.setColor(IndexedColors.GREEN.getIndex());
cellStyle.setFont(font);
// 文件名
cell = row.createCell(cellNum++);
cell.setCellValue(data.getFileName());
// 簽名時(shí)間
cell = row.createCell(cellNum++);
cell.setCellValue(null == data.getSignDate() ? "" : data.getSignDate());
// 有效期
cell = row.createCell(cellNum++);
cell.setCellValue(null == data.getValidBefore() ? "" : data.getValidBefore());
// 有效期
cell = row.createCell(cellNum++);
cell.setCellValue(null == data.getValidAfter() ? "" : data.getValidAfter());
//主題
cell = row.createCell(cellNum++);
cell.setCellValue(null == data.getSubject() ? "" : data.getSubject());
//序列號(hào)
cell = row.createCell(cellNum++);
cell.setCellValue(null == data.getSerialNumber() ? "" : data.getSerialNumber());
//是否通過驗(yàn)簽
cell = row.createCell(cellNum);
if (data.getIsEffective()) {
cell.setCellValue("簽名有效");
} else {
cell.setCellValue("簽名無效");
cell.setCellStyle(cellStyle);
}
}
public static void writeExcel(List<ExcelDataVO> dataVOList, String exportFilePath) {
// 寫入數(shù)據(jù)到工作簿對(duì)象內(nèi)
Workbook workbook = ExcelWriter.exportData(dataVOList);
// 以文件的形式輸出工作簿對(duì)象
FileOutputStream fileOut = null;
try {
File exportFile = new File(exportFilePath);
if (!exportFile.exists()) {
boolean newFile = exportFile.createNewFile();
if (!newFile) {
System.out.println("文件創(chuàng)建失敗");
}
}
fileOut = new FileOutputStream(exportFilePath);
workbook.write(fileOut);
fileOut.flush();
} catch (Exception e) {
System.out.println("輸出Excel時(shí)發(fā)生錯(cuò)誤,錯(cuò)誤原因:" + e.getMessage());
} finally {
try {
if (null != fileOut) {
fileOut.close();
}
workbook.close();
} catch (IOException e) {
System.out.println("關(guān)閉輸出流時(shí)發(fā)生錯(cuò)誤,錯(cuò)誤原因:" + e.getMessage());
}
}
}
}
public class VerifySignature {
private static String fromDirPath;
private static String toFilePath;
public static void main(String[] args) {
final JFrame jf = new JFrame("測(cè)試窗口");
jf.setSize(400, 250);
jf.setLocationRelativeTo(null);
jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
// 創(chuàng)建文本區(qū)域, 用于顯示相關(guān)信息
final JTextArea msgTextArea = new JTextArea(10, 30);
msgTextArea.setLineWrap(true);
panel.add(msgTextArea);
JButton openBtn = new JButton("選擇文件路徑");
openBtn.addActionListener(e -> showFileOpenDialog(jf, msgTextArea));
panel.add(openBtn);
JButton saveBtn = new JButton("結(jié)果保存位置");
saveBtn.addActionListener(e -> showFileSaveDialog(jf, msgTextArea));
panel.add(saveBtn);
jf.setContentPane(panel);
jf.setVisible(true);
JButton enSureBtn = new JButton("確認(rèn)");
enSureBtn.addActionListener(e -> enSureListener(jf));
panel.add(enSureBtn);
jf.setContentPane(panel);
jf.setVisible(true);
}
/*
* 打開文件
*/
private static void showFileOpenDialog(Component parent, JTextArea msgTextArea) {
// 創(chuàng)建一個(gè)默認(rèn)的文件選取器
JFileChooser fileChooser = new JFileChooser();
// 設(shè)置默認(rèn)顯示的文件夾為當(dāng)前文件夾
fileChooser.setCurrentDirectory(new File("."));
// 設(shè)置文件選擇的模式(只選文件、只選文件夾、文件和文件均可選)
fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
// 設(shè)置是否允許多選
fileChooser.setMultiSelectionEnabled(false);
// // 添加可用的文件過濾器(FileNameExtensionFilter 的第一個(gè)參數(shù)是描述, 后面是需要過濾的文件擴(kuò)展名 可變參數(shù))
// fileChooser.addChoosableFileFilter(new FileNameExtensionFilter("zip(*.zip, *.rar)", "zip", "rar"));
//
// // 設(shè)置默認(rèn)使用的文件過濾器
// fileChooser.setFileFilter(new FileNameExtensionFilter("image(*.jpg, *.png, *.gif)", "jpg", "png", "gif"));
// 打開文件選擇框(線程將被阻塞, 直到選擇框被關(guān)閉)
int result = fileChooser.showOpenDialog(parent);
if (result == JFileChooser.APPROVE_OPTION) {
// 如果點(diǎn)擊了"確定", 則獲取選擇的文件路徑
File file = fileChooser.getSelectedFile();
fromDirPath = file.getAbsolutePath();
msgTextArea.append("選擇源文件: " + fromDirPath + "\n\n");
}
}
/*
* 選擇文件保存路徑
*/
private static void showFileSaveDialog(Component parent, JTextArea msgTextArea) {
// 創(chuàng)建一個(gè)默認(rèn)的文件選取器
JFileChooser fileChooser = new JFileChooser();
//把時(shí)間戳經(jīng)過處理得到期望格式的時(shí)間
Date date = new Date();
SimpleDateFormat format0 = new SimpleDateFormat("yyyyMMddHHmmss");
String now = format0.format(date.getTime());
// 設(shè)置打開文件選擇框后默認(rèn)輸入的文件名
fileChooser.setSelectedFile(new File(now + ".xlsx"));
// 打開文件選擇框(線程將被阻塞, 直到選擇框被關(guān)閉)
int result = fileChooser.showSaveDialog(parent);
if (result == JFileChooser.APPROVE_OPTION) {
// 如果點(diǎn)擊了"保存", 則獲取選擇的保存路徑
File file = fileChooser.getSelectedFile();
toFilePath = file.getAbsolutePath();
msgTextArea.append("結(jié)果文件路徑: " + toFilePath + "\n\n");
}
}
//找到需要的內(nèi)容
public final static Pattern pattern = Pattern.compile("\\[Subject\\].*?O=(.*?),.*?\\[Issuer\\](.*?)\\[Serial Number\\](.*?)\\[Not Before\\](.*?)\\[Not After\\](.*?)\\[Thumbprint\\](.*?)");
// 剔除特殊字符
public final static Pattern replacePattern = Pattern.compile("\t|\r|\n");
/**
* 查找某個(gè)路徑下的所有pdf文件
*
* @return 所有的pdf絕對(duì)路徑
*/
public static HashSet<String> listDir(String path) {
HashSet<String> FileNameString = new HashSet<String>();
File file = new File(path); //獲取其file對(duì)象
File[] fs = file.listFiles(); //遍歷path下的文件和目錄,放在File數(shù)組中
if (fs == null) {
System.out.println(path + "路徑下沒有文件");
return null;
}
//遍歷File[]數(shù)組
for (File f : fs) {
String fileName = String.valueOf(f);
if (!f.isDirectory() && fileName.toLowerCase().endsWith(".pdf")) //若非目錄(即文件),則打印
FileNameString.add(fileName);
}
return FileNameString;
}
/**
* 檢驗(yàn)pdf文件是否簽名
*
* @param filePath pdf文件絕對(duì)路徑
*/
public static ExcelDataVO checkPdf(String filePath) {
//創(chuàng)建PdfDocument實(shí)例
PdfDocument doc = new PdfDocument();
//創(chuàng)建結(jié)果集
ExcelDataVO excelDataVO = new ExcelDataVO();
//文件名,注意windows下應(yīng)該是\\,linux下是/
String fileName = filePath.substring(filePath.lastIndexOf("\\") + 1);
excelDataVO.setFileName(fileName);
//加載含有簽名的PDF文件
doc.loadFromFile(filePath);
//獲取域集合
PdfFormWidget pdfFormWidget = (PdfFormWidget) doc.getForm();
PdfFormFieldWidgetCollection pdfFormFieldWidgetCollection = pdfFormWidget.getFieldsWidget();
// int countCollection = pdfFormFieldWidgetCollection.getCount();
// System.out.println("共發(fā)現(xiàn)" + countCollection + "個(gè)域");
//遍歷域
for (int i = 0; i < pdfFormFieldWidgetCollection.getCount(); i++) {
//判定是否為簽名域
if (pdfFormFieldWidgetCollection.get(i) instanceof PdfSignatureFieldWidget) {
//獲取簽名域
PdfSignatureFieldWidget signatureFieldWidget = (PdfSignatureFieldWidget) pdfFormFieldWidgetCollection.get(i);
//獲取簽名時(shí)間
PdfSignature signature = signatureFieldWidget.getSignature();
excelDataVO.setSignDate(String.valueOf(signature.getDate()));
//獲取簽名的內(nèi)容
PdfCertificate certificate = signature.getCertificate();
// System.out.println("Issuer:" + certificate.getIssuer());
// System.out.println("Subject:" + certificate.getSubject());
// System.out.println("---------");
// excelDataVO.setSubject(String.valueOf(certificate.getSubject()));
String certificateString = certificate.toString();
Matcher m = replacePattern.matcher(certificateString);
certificateString = m.replaceAll("");
Matcher matcher = pattern.matcher(certificateString);
while (matcher.find()) {
// String group = matcher.group(0);
String subject = matcher.group(1);
// String issuer = matcher.group(2);
String serialNumber = matcher.group(3);
String before = matcher.group(4);
String after = matcher.group(5);
// String sha1 = matcher.group(6);
excelDataVO.setSubject(subject);
excelDataVO.setSerialNumber(serialNumber);
excelDataVO.setValidBefore(before);
excelDataVO.setValidAfter(after);
}
//判定簽名是否有效
boolean result = signature.verifySignature();
excelDataVO.setIsEffective(result);
if (result) {
return excelDataVO;
}
}
}
return excelDataVO;
}
/*
* 開始執(zhí)行業(yè)務(wù)邏輯
*/
private static void enSureListener(JFrame parent) {
parent.dispose();
System.out.println("開始驗(yàn)簽...");
//從某個(gè)路徑下獲取所有的pdf文件路徑
HashSet<String> filePaths = listDir(fromDirPath);
if (filePaths == null) {
return;
}
List<ExcelDataVO> excelDataVOS = new ArrayList<>();
for (String filePath : filePaths) {
ExcelDataVO excelDataVO = checkPdf(filePath);
excelDataVOS.add(excelDataVO);
}
ExcelWriter.writeExcel(excelDataVOS, toFilePath);
System.out.println("驗(yàn)簽完成...");
}
}
以上就是用Java驗(yàn)證pdf文件的電子章簽名的詳細(xì)內(nèi)容,更多關(guān)于Java驗(yàn)證pdf文件的電子章簽名的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- Java 使用openoffice進(jìn)行word轉(zhuǎn)換為pdf的方法步驟
- Java使用iTextPDF生成PDF文件的實(shí)現(xiàn)方法
- Java 實(shí)現(xiàn)word模板轉(zhuǎn)為pdf
- java 后端生成pdf模板合并單元格表格的案例
- java 畫pdf用itext調(diào)整表格寬度、自定義各個(gè)列寬的方法
- java 用itext設(shè)置pdf紙張大小操作
- Java生成pdf文件或jpg圖片的案例講解
- 基于Java SWFTools實(shí)現(xiàn)把pdf轉(zhuǎn)成swf
- Java實(shí)現(xiàn)Word/Pdf/TXT轉(zhuǎn)html的示例
- Java pdf和jpg互轉(zhuǎn)案例
- Java實(shí)現(xiàn)圖片轉(zhuǎn)換PDF文件的示例代碼
- Java 基于Spire.Cloud.SDK for Java在PDF中繪制形狀
- Java 在PDF中添加騎縫章示例解析
- Java 在PDF中繪制形狀的兩種方法
- 教你怎么用Java通過關(guān)鍵字修改pdf
相關(guān)文章
Spring?Cloud?Gateway?整合?knife4j?聚合接口文檔功能
這篇文章主要介紹了Spring?Cloud?Gateway?整合?knife4j?聚合接口文檔的相關(guān)知識(shí),我們可以基于?Spring?Cloud?Gateway?網(wǎng)關(guān)?+?nacos?+?knife4j?對(duì)所有微服務(wù)項(xiàng)目的接口文檔進(jìn)行聚合,從而實(shí)現(xiàn)我們想要的文檔管理功能,需要的朋友可以參考下2022-02-02
解決springmvc整合Mybatis的Log4j日志輸出問題
這篇文章主要介紹了解決springmvc整合Mybatis的Log4j日志輸出問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
劍指Offer之Java算法習(xí)題精講二叉樹專項(xiàng)訓(xùn)練
跟著思路走,之后從簡(jiǎn)單題入手,反復(fù)去看,做過之后可能會(huì)忘記,之后再做一次,記不住就反復(fù)做,反復(fù)尋求思路和規(guī)律,慢慢積累就會(huì)發(fā)現(xiàn)質(zhì)的變化2022-03-03
spring boot實(shí)戰(zhàn)教程之shiro session過期時(shí)間詳解
這篇文章主要給大家介紹了關(guān)于spring boot實(shí)戰(zhàn)教程之shiro session過期時(shí)間的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧。2017-10-10
Springboot使用@Cacheable注解實(shí)現(xiàn)數(shù)據(jù)緩存
本文介紹如何在Springboot中通過@Cacheable注解實(shí)現(xiàn)數(shù)據(jù)緩存,在每次調(diào)用添加了@Cacheable注解的方法時(shí),Spring 會(huì)檢查指定參數(shù)的指定目標(biāo)方法是否已經(jīng)被調(diào)用過,文中有詳細(xì)的代碼示例,需要的朋友可以參考下2023-10-10
關(guān)于maven配置項(xiàng)目一直提示程序包不存在以及scope的坑
這篇文章主要介紹了關(guān)于maven配置項(xiàng)目一直提示程序包不存在以及scope的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11

