驗證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 {
        // 類裝載時就載入指定好的表頭信息,如有需要,可以考慮做成動態生成的表頭
        CELL_HEADS = new ArrayList<>();
        CELL_HEADS.add("文件名");
        CELL_HEADS.add("簽名時間");
        CELL_HEADS.add("有效期");
        CELL_HEADS.add("有效期");
        CELL_HEADS.add("簽名機構");
        CELL_HEADS.add("序列號");
        CELL_HEADS.add("是否通過驗簽");
    }

    /**
     * 生成Excel並寫入數據信息
     *
     * @param dataList 數據列表
     * @return 寫入數據后的工作簿對象
     */
    public static Workbook exportData(List<ExcelDataVO> dataList) {
        // 生成xlsx的Excel
        Workbook workbook = new SXSSFWorkbook();

        // 如需生成xls的Excel,請使用下面的工作簿對象,注意后續輸出時文件后綴名也需更改為xls
        //Workbook workbook = new HSSFWorkbook();

        // 生成Sheet表,寫入第一行的表頭
        Sheet sheet = buildDataSheet(workbook);
        //構建每行的數據內容
        int rowNum = 1;
        for (ExcelDataVO data : dataList) {
            if (data == null) {
                continue;
            }
            //輸出行數據
            Row row = sheet.createRow(rowNum++);
            convertDataToRow(workbook, data, row);
        }
        return workbook;
    }

    /**
     * 生成sheet表,並寫入第一行數據(表頭)
     *
     * @param workbook 工作簿對象
     * @return 已經寫入表頭的Sheet
     */
    private static Sheet buildDataSheet(Workbook workbook) {
        Sheet sheet = workbook.createSheet();
        // 設置表頭寬度
        for (int i = 0; i < CELL_HEADS.size(); i++) {
            sheet.setColumnWidth(i, 4000);
        }
        // 設置默認行高
        sheet.setDefaultRowHeight((short) 400);
        // 構建頭單元格樣式
        CellStyle cellStyle = buildHeadCellStyle(sheet.getWorkbook());
        // 寫入第一行各列的數據
        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;
    }

    /**
     * 設置第一行表頭的樣式
     *
     * @param workbook 工作簿對象
     * @return 單元格樣式對象
     */
    private static CellStyle buildHeadCellStyle(Workbook workbook) {
        CellStyle style = workbook.createCellStyle();
        //對齊方式設置
        style.setAlignment(HorizontalAlignment.CENTER);
        //邊框顏色和寬度設置
        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()); // 上邊框
        //設置背景顏色
        style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        //粗體字設置
        Font font = workbook.createFont();
        font.setBold(true);
        style.setFont(font);
        return style;
    }


    /**
     * 將數據轉換成行
     *
     * @param data 源數據
     * @param row  行對象
     */
    private static void convertDataToRow(Workbook workbook, ExcelDataVO data, Row row) {

        int cellNum = 0;
        Cell cell;

        //對特殊數值設置顏色
        CellStyle cellStyle = workbook.createCellStyle();

        //字體設置
        Font font = workbook.createFont();
        font.setBold(true);
        font.setColor(IndexedColors.GREEN.getIndex());
        cellStyle.setFont(font);

        // 文件名
        cell = row.createCell(cellNum++);
        cell.setCellValue(data.getFileName());

        // 簽名時間
        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());
        //序列號
        cell = row.createCell(cellNum++);
        cell.setCellValue(null == data.getSerialNumber() ? "" : data.getSerialNumber());
        //是否通過驗簽
        cell = row.createCell(cellNum);
        if (data.getIsEffective()) {
            cell.setCellValue("簽名有效");
        } else {
            cell.setCellValue("簽名無效");
            cell.setCellStyle(cellStyle);

        }
    }

    public static void writeExcel(List<ExcelDataVO> dataVOList, String exportFilePath) {


        // 寫入數據到工作簿對象內
        Workbook workbook = ExcelWriter.exportData(dataVOList);

        // 以文件的形式輸出工作簿對象
        FileOutputStream fileOut = null;
        try {
            File exportFile = new File(exportFilePath);
            if (!exportFile.exists()) {
                boolean newFile = exportFile.createNewFile();
                if (!newFile) {
                    System.out.println("文件創建失敗");
                }
            }

            fileOut = new FileOutputStream(exportFilePath);
            workbook.write(fileOut);
            fileOut.flush();
        } catch (Exception e) {
            System.out.println("輸出Excel時發生錯誤,錯誤原因:" + e.getMessage());
        } finally {
            try {
                if (null != fileOut) {
                    fileOut.close();
                }
                workbook.close();
            } catch (IOException e) {
                System.out.println("關閉輸出流時發生錯誤,錯誤原因:" + e.getMessage());
            }
        }
    }
}


public class VerifySignature {
    private static String fromDirPath;
    private static String toFilePath;

    public static void main(String[] args) {
        final JFrame jf = new JFrame("測試窗口");
        jf.setSize(400, 250);
        jf.setLocationRelativeTo(null);
        jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        JPanel panel = new JPanel();

        // 創建文本區域, 用於顯示相關信息
        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("結果保存位置");
        saveBtn.addActionListener(e -> showFileSaveDialog(jf, msgTextArea));
        panel.add(saveBtn);

        jf.setContentPane(panel);
        jf.setVisible(true);

        JButton enSureBtn = new JButton("確認");
        enSureBtn.addActionListener(e -> enSureListener(jf));
        panel.add(enSureBtn);

        jf.setContentPane(panel);
        jf.setVisible(true);
    }

    /*
     * 打開文件
     */
    private static void showFileOpenDialog(Component parent, JTextArea msgTextArea) {
        // 創建一個默認的文件選取器
        JFileChooser fileChooser = new JFileChooser();

        // 設置默認顯示的文件夾為當前文件夾
        fileChooser.setCurrentDirectory(new File("."));

        // 設置文件選擇的模式(只選文件、只選文件夾、文件和文件均可選)
        fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);

        // 設置是否允許多選
        fileChooser.setMultiSelectionEnabled(false);

//        // 添加可用的文件過濾器(FileNameExtensionFilter 的第一個參數是描述, 后面是需要過濾的文件擴展名 可變參數)
//        fileChooser.addChoosableFileFilter(new FileNameExtensionFilter("zip(*.zip, *.rar)", "zip", "rar"));
//
//        // 設置默認使用的文件過濾器
//        fileChooser.setFileFilter(new FileNameExtensionFilter("image(*.jpg, *.png, *.gif)", "jpg", "png", "gif"));

        // 打開文件選擇框(線程將被阻塞, 直到選擇框被關閉)
        int result = fileChooser.showOpenDialog(parent);

        if (result == JFileChooser.APPROVE_OPTION) {
            // 如果點擊了"確定", 則獲取選擇的文件路徑
            File file = fileChooser.getSelectedFile();
            fromDirPath = file.getAbsolutePath();

            msgTextArea.append("選擇源文件: " + fromDirPath + "\n\n");
        }
    }

    /*
     * 選擇文件保存路徑
     */
    private static void showFileSaveDialog(Component parent, JTextArea msgTextArea) {
        // 創建一個默認的文件選取器
        JFileChooser fileChooser = new JFileChooser();

        //把時間戳經過處理得到期望格式的時間
        Date date = new Date();
        SimpleDateFormat format0 = new SimpleDateFormat("yyyyMMddHHmmss");
        String now = format0.format(date.getTime());

        // 設置打開文件選擇框后默認輸入的文件名
        fileChooser.setSelectedFile(new File(now + ".xlsx"));

        // 打開文件選擇框(線程將被阻塞, 直到選擇框被關閉)
        int result = fileChooser.showSaveDialog(parent);

        if (result == JFileChooser.APPROVE_OPTION) {
            // 如果點擊了"保存", 則獲取選擇的保存路徑
            File file = fileChooser.getSelectedFile();
            toFilePath = file.getAbsolutePath();
            msgTextArea.append("結果文件路徑: " + toFilePath + "\n\n");
        }
    }

    //找到需要的內容
    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");


    /**
     * 查找某個路徑下的所有pdf文件
     *
     * @return 所有的pdf絕對路徑
     */
    public static HashSet<String> listDir(String path) {
        HashSet<String> FileNameString = new HashSet<String>();
        File file = new File(path);        //獲取其file對象
        File[] fs = file.listFiles();    //遍歷path下的文件和目錄,放在File數組中
        if (fs == null) {
            System.out.println(path + "路徑下沒有文件");
            return null;
        }

        //遍歷File[]數組
        for (File f : fs) {
            String fileName = String.valueOf(f);
            if (!f.isDirectory() && fileName.toLowerCase().endsWith(".pdf"))        //若非目錄(即文件),則打印
                FileNameString.add(fileName);
        }
        return FileNameString;
    }

    /**
     * 檢驗pdf文件是否簽名
     *
     * @param filePath pdf文件絕對路徑
     */
    public static ExcelDataVO checkPdf(String filePath) {
        //創建PdfDocument實例
        PdfDocument doc = new PdfDocument();

        //創建結果集
        ExcelDataVO excelDataVO = new ExcelDataVO();

        //文件名,注意windows下應該是\\,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("共發現" + countCollection + "個域");

        //遍歷域
        for (int i = 0; i < pdfFormFieldWidgetCollection.getCount(); i++) {
            //判定是否為簽名域
            if (pdfFormFieldWidgetCollection.get(i) instanceof PdfSignatureFieldWidget) {
                //獲取簽名域
                PdfSignatureFieldWidget signatureFieldWidget = (PdfSignatureFieldWidget) pdfFormFieldWidgetCollection.get(i);
                //獲取簽名時間
                PdfSignature signature = signatureFieldWidget.getSignature();
                excelDataVO.setSignDate(String.valueOf(signature.getDate()));

                //獲取簽名的內容
                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;
    }

    /*
     * 開始執行業務邏輯
     */
    private static void enSureListener(JFrame parent) {
        parent.dispose();
        System.out.println("開始驗簽...");

        //從某個路徑下獲取所有的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("驗簽完成...");

    }

}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM