用Java验证pdf文件的电子章签名

2022-07-25,,,,

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("验签完成...");

  }

}

以上就是用java验证pdf文件的电子章签名的详细内容,更多关于java验证pdf文件的电子章签名的资料请关注其它相关文章!

《用Java验证pdf文件的电子章签名.doc》

下载本文的Word格式文档,以方便收藏与打印。