itextpdf 提取数字签名签章图片

itextpdf 提取数字签名签章图片

因业务需求,需要对签名的pdf进行签名验证,政府的电子验证的功能是除了提取pdf表单内容外,还可以提取签章的图片,研究了好久,特此记录一下。

/**
 * 获取pdf签名图片信息
 * @param path          pdf文件
 * @param fileBasePath  图片保存基础路径
 * @return              提取结果
 */
public static PdfSignInfo getImageFromPdf(String path, String fileBasePath) {
    PdfSignInfo pdfSignInfo = new PdfSignInfo();
    try {
        FileInputStream fis = new FileInputStream(path);
        PdfReader reader = new PdfReader(fis);

        String fileName = path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf("."));
        if (!fileBasePath.endsWith("/")) {
            fileBasePath = fileBasePath + "/";
        }
        fileBasePath = fileBasePath + fileName + "/";
        File file = new File(fileBasePath);
        file.mkdirs();

        pdfSignInfo.setName(path.substring(path.lastIndexOf("/") + 1));

        Field rsaDataField = PdfPKCS7.class.getDeclaredField("RSAdata");
        rsaDataField.setAccessible(true);

        int numberOfPages = reader.getNumberOfPages();
        // pdf 页码数量
        log.debug("page size: {}", numberOfPages);

        int xrefSize = reader.getXrefSize();

        ExtImageRenderListener listener = new ExtImageRenderListener();
        listener.setBasePath(fileBasePath);

        // 获取acro字段
        AcroFields fields = reader.getAcroFields();
        Rectangle pageSize = reader.getPageSize(numberOfPages - 1);
        // pdf 作用域尺寸(全屏)
        log.info("pdf rectangle: {},{},{},{}", pageSize.getLeft(), pageSize.getBottom(), pageSize.getRight(), pageSize.getTop());

        // 获取签名名称
        ArrayList<String> signatureNames = fields.getSignatureNames();
        for (String name : signatureNames) {
            // 指定提供者 获取签名的pkcs7数据
            PdfPKCS7 pkcs7 = fields.verifySignature(name);
            X509Certificate certificate = pkcs7.getSigningCertificate();

            PdfSignInfo.SignatureDetail info = new PdfSignInfo.SignatureDetail();

            String[] appearanceStates = fields.getAppearanceStates(name);
            // 表单域的位置
            List<AcroFields.FieldPosition> fieldPositions = fields.getFieldPositions(name);
            for (AcroFields.FieldPosition fieldPosition : fieldPositions) {
                Rectangle position = fieldPosition.position;
                log.info("field {} position: {}, {}, {}, {}, {}", name, fieldPosition.page,
                        position.getLeft(), position.getBottom(), position.getRight(), position.getTop());
                info.page = fieldPosition.page;
                info.llx = position.getLeft();
                info.lly = position.getBottom();
                info.urx = position.getRight();
                info.ury = position.getTop();
            }

            AcroFields.Item fieldItem = fields.getFieldItem(name);
            int size = fieldItem.size();
            for (int i = 0; i < size; i++) {
                PdfDictionary value = fieldItem.getValue(i);
                Set<PdfName> keys = value.getKeys();
                for (PdfName key : keys) {
                    String keyName = PdfName.decodeName(new String(key.getBytes()));
                    if ("Rect".equalsIgnoreCase(keyName)) {
                        log.debug("签名域的坐标为:{}", value.get(key));
                    }
                }
                PdfObject pdfObject = value.get(PdfName.AP);
                if (pdfObject != null) {
                    PdfDictionary dictionary = (PdfDictionary) pdfObject;
                    PdfIndirectReference ref = (PdfIndirectReference) dictionary.get(PdfName.N);
                    int number = ref.getNumber();
                    log.debug("fieldName: {}, idx: {}", name, number);

                    // 根据ap 获取签章图片
                    PdfObject pdfObjectRelease = reader.getPdfObject(number);
                    if (pdfObjectRelease instanceof PdfStream) {
                        PdfStream s = (PdfStream) pdfObjectRelease;
                        PdfDictionary resources = s.getAsDict(PdfName.RESOURCES);
                        listener.setI(number);
                        try {
                            PdfContentStreamProcessor processor = new PdfContentStreamProcessor(listener);
                            processor.processContent(ContentByteUtils.getContentBytesFromContentObject(s), resources);
                            info.filePath = fileBasePath + number + ".png";
                        } catch (Exception ignore) {}
                    }
                }

            }

            // 签名信息
            info.name = name;
            info.time = pkcs7.getSignDate().getTime();
            info.notBefore = certificate.getNotBefore();
            info.notAfter = certificate.getNotAfter();
            info.dnName = certificate.getSubjectDN().getName();
            info.serialNumber = certificate.getSerialNumber();
            info.pubKey = Base64Encoder.encode(certificate.getPublicKey().getEncoded());
            info.pubKeyFormat = certificate.getPublicKey().getFormat();
            info.sigAlgName = certificate.getSigAlgName();
            info.userDnName = certificate.getIssuerDN().getName();
            info.reason = pkcs7.getReason();
            info.location = pkcs7.getLocation();

            info.res = pkcs7.verify();

            pdfSignInfo.getSignatureDetails().add(info);
        }
        log.info(JSON.toJSONString(pdfSignInfo));
    } catch (Exception e) {
        e.printStackTrace();
    }
    return pdfSignInfo;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PdfSignInfo {

    private String name;

    private List<SignatureDetail> signatureDetails = new ArrayList<>();

    public static class SignatureDetail {
        public String name;
        // 签名日期时间
        public Date time;
        // 有效期开始时间
        public Date notBefore;
        // 有效期
        public Date notAfter;
        // 证书名称
        public String dnName;
        // 序列号
        public BigInteger serialNumber;
        // 证书格式 // 证书颁发者
        public String pubKey, pubKeyFormat, sigAlgName, userDnName;
        public boolean res;
        public String imageContent, imagePath;

        public String reason, location;

        public int page;
        public float llx, lly, urx, ury;

        public String filePath;
    }

}
@Slf4j
@Data
public class ExtImageRenderListener implements RenderListener {
    private int i;
    private String basePath;
    @Override
    public void beginTextBlock() {

    }

    public void setBasePath(String basePath) {
        if (basePath.endsWith("/")) {
            this.basePath = basePath;
        } else {
            this.basePath = basePath + "/";
        }
    }

    @Override
    public void renderText(TextRenderInfo renderInfo) {

    }

    @Override
    public void endTextBlock() {

    }

    PdfDictionary resources;

    List<List<Integer>> numbers = new ArrayList<>();

    @SneakyThrows
    @Override
    public void renderImage(ImageRenderInfo renderInfo) {
        PdfImageObject image = renderInfo.getImage();
        if (image == null) {
            log.debug("Image {} could not be read", renderInfo.getRef().getNumber());
            return;
        }

        BufferedImage bufferedImage = image.getBufferedImage();
        if (bufferedImage != null) {
            log.debug("bufferedImage: {}", bufferedImage.getHeight());
            writeImage(bufferedImage);
        }

//        PRStream maskStream = (PRStream) image.getDictionary().getAsStream(PdfName.SMASK);
//        if (maskStream != null) {
//            PdfImageObject pdfImageObject = new PdfImageObject(maskStream);
//            log.debug("mask image: {}", pdfImageObject.getFileType());
//            BufferedImage mask = pdfImageObject.getBufferedImage();
//            writeImage(mask);
//        }
    }

    private void writeImage(BufferedImage bufferedImage) {
        try {
            File file = new File(basePath + i + ".png");
            if (file.exists()) {
                return;
            }
            log.debug("generated image: {}", file.getPath());
            ImageIO.write(bufferedImage, "png", file);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值