在Java语言环境中完成数字签名主要基于itext-pdf、PDFBox两种工具,itext-pdf受商业限制,应用于商业服务中需要购买授权。PDFBox是apache基金会开源项目,基于apache2.0开源协议,不受商业限制,开发者可放心使用。以下是基于PDFBox的数字签名源码,使用该源码可使用PDFBox对PDF格式的文件进行数字。此外,完成数字签名还生成数字证书,全流程的数字签名示例可下载开源项目模拟数字签名全流程。开源数字签名访问地址:https://gitee.com/kaifangqian

`package org.resrun.sdk.pdfbox;

import org.resrun.sdk.pdfbox.vo.AssinaturaModel;

import org.apache.pdfbox.Loader;

import org.apache.pdfbox.cos.COSName;

import org.apache.pdfbox.io.IOUtils;

import org.apache.pdfbox.pdmodel.PDDocument;

import org.apache.pdfbox.pdmodel.PDPage;

import org.apache.pdfbox.pdmodel.PDPageContentStream;

import org.apache.pdfbox.pdmodel.PDResources;

import org.apache.pdfbox.pdmodel.common.PDRectangle;

import org.apache.pdfbox.pdmodel.common.PDStream;

import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;

import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;

import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;

import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;

import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;

import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;

import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureOptions;

import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;

import org.apache.pdfbox.pdmodel.interactive.form.PDField;

import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;

import org.apache.pdfbox.util.Matrix;

import java.awt.geom.AffineTransform;

import java.awt.geom.Rectangle2D;

import java.io.ByteArrayInputStream;

import java.io.ByteArrayOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.security.KeyStoreException;

import java.security.NoSuchAlgorithmException;

import java.security.UnrecoverableKeyException;

import java.security.cert.CertificateException;

import java.util.Calendar;

import java.util.List;

public class AssinaturaPDF extends SignatureBase {

private SignatureOptions signatureOptions;

private AssinaturaModel assinatura;
public AssinaturaPDF(AssinaturaModel assinaturaModel) throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, IOException, CertificateException {
super(assinaturaModel.getCertInfo());
this.assinatura = assinaturaModel;
if (assinaturaModel.getTsa() != null && !assinaturaModel.getTsa().equals("")) {
setTsaUrl(assinaturaModel.getTsa());
}
} public byte [] assina() throws Exception { if (assinatura.getPdf() == null || assinatura.getPdf().length == 0) {
throw new Exception("pdf file null");
} try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
PDDocument doc = Loader.loadPDF(assinatura.getPdf())) {
if (doc.isEncrypted()) {
try {
doc.setAllSecurityToBeRemoved(true);
} catch (Exception e) {
throw new Exception("pdf passwd error", e);
}
} int accessPermissions = SigUtils.getMDPPermission(doc);
if (accessPermissions == 1)
{
throw new IllegalStateException("No changes to the document are permitted due to DocMDP transform parameters dictionary");
} PDSignature signature = null;
PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm(null);
PDRectangle rect = null; // TODO 签名域的位置 可能需要再计算
Rectangle2D humanRect = new Rectangle2D.Float(assinatura.getPosition().getParseX(), assinatura.getPosition().getParseY(),
assinatura.getPosition().getSignWidthFloat(), assinatura.getPosition().getSignHeightFloat()); // sign a PDF with an existing empty signature, as created by the CreateEmptySignatureForm example.
if (acroForm != null)
{
signature = findExistingSignature(acroForm, assinatura.getSignatureKey());
if (signature != null)
{
rect = acroForm.getField(assinatura.getSignatureKey()).getWidgets().get(0).getRectangle();
}
} if (signature == null)
{
// create signature dictionary
signature = new PDSignature();
} if (rect == null)
{
rect = createSignatureRectangle(doc, humanRect);
} if (doc.getVersion() >= 1.5f && accessPermissions == 0)
{
SigUtils.setMDPPermission(doc, signature, 2);
} if (acroForm != null && acroForm.getNeedAppearances())
{
// PDFBOX-3738 NeedAppearances true results in visible signature becoming invisible
// with Adobe Reader
if (acroForm.getFields().isEmpty())
{
// we can safely delete it if there are no fields
acroForm.getCOSObject().removeItem(COSName.NEED_APPEARANCES);
// note that if you've set MDP permissions, the removal of this item
// may result in Adobe Reader claiming that the document has been changed.
// and/or that field content won't be displayed properly.
// ==> decide what you prefer and adjust your code accordingly.
}
else
{
System.out.println("/NeedAppearances is set, signature may be ignored by Adobe Reader");
}
} // default filter
signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE); // subfilter for basic and PAdES Part 2 signatures
signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED); signature.setName("Name");
signature.setLocation("Location");
signature.setReason("Reason");

// the signing date, needed for valid signature

signature.setSignDate(Calendar.getInstance());

        signatureOptions = new SignatureOptions();
signatureOptions.setVisualSignature(createVisualSignatureTemplate(doc, 0, rect, signature));
signatureOptions.setPage(assinatura.getPosition().getPage());

// signatureOptions.setPreferredSignatureSize(SignatureOptions.DEFAULT_SIGNATURE_SIZE * 2);

        doc.addSignature(signature, this, signatureOptions);

// doc.saveIncremental(bos);

doc.saveIncremental(bos);

IOUtils.closeQuietly(signatureOptions);

doc.close();

return bos.toByteArray();

// return null;

}

}

private InputStream createVisualSignatureTemplate(PDDocument srcDoc, int pageNum,

PDRectangle rect, PDSignature signature) throws IOException

{

try (PDDocument doc = new PDDocument())

{

PDPage page = new PDPage(srcDoc.getPage(pageNum).getMediaBox());

doc.addPage(page);

PDAcroForm acroForm = new PDAcroForm(doc);

doc.getDocumentCatalog().setAcroForm(acroForm);

PDSignatureField signatureField = new PDSignatureField(acroForm);

PDAnnotationWidget widget = signatureField.getWidgets().get(0);

List acroFormFields = acroForm.getFields();

acroForm.setSignaturesExist(true);

acroForm.setAppendOnly(true);

acroForm.getCOSObject().setDirect(true);

acroFormFields.add(signatureField);

        widget.setRectangle(rect);

        // from PDVisualSigBuilder.createHolderForm()
PDStream stream = new PDStream(doc);
PDFormXObject form = new PDFormXObject(stream);
PDResources res = new PDResources();
form.setResources(res);
form.setFormType(1);
PDRectangle bbox = new PDRectangle(rect.getWidth(), rect.getHeight());
float height = bbox.getHeight();
Matrix initialScale = null;
switch (srcDoc.getPage(pageNum).getRotation())
{
case 90:
form.setMatrix(AffineTransform.getQuadrantRotateInstance(1));
initialScale = Matrix.getScaleInstance(bbox.getWidth() / bbox.getHeight(), bbox.getHeight() / bbox.getWidth());
height = bbox.getWidth();
break;
case 180:
form.setMatrix(AffineTransform.getQuadrantRotateInstance(2));
break;
case 270:
form.setMatrix(AffineTransform.getQuadrantRotateInstance(3));
initialScale = Matrix.getScaleInstance(bbox.getWidth() / bbox.getHeight(), bbox.getHeight() / bbox.getWidth());
height = bbox.getWidth();
break;
case 0:
default:
break;
}
form.setBBox(bbox); // from PDVisualSigBuilder.createAppearanceDictionary()
PDAppearanceDictionary appearance = new PDAppearanceDictionary();
appearance.getCOSObject().setDirect(true);
PDAppearanceStream appearanceStream = new PDAppearanceStream(form.getCOSObject());
appearance.setNormalAppearance(appearanceStream);
widget.setAppearance(appearance); try (PDPageContentStream cs = new PDPageContentStream(doc, appearanceStream))
{
// for 90° and 270° scale ratio of width / height
// not really sure about this
// why does scale have no effect when done in the form matrix???
if (initialScale != null)
{
cs.transform(initialScale);
} // show background (just for debugging, to see the rect size + position)

// cs.setNonStrokingColor(Color.yellow);

// cs.addRect(-5000, -5000, 10000, 10000);

cs.fill();

            if (assinatura.getSignatureImage() != null)
{ PDImageXObject img = PDImageXObject.createFromByteArray(doc,assinatura.getSignatureImage(),"test");
int imgHeight = img.getHeight();
int imgWidth = img.getWidth();
cs.saveGraphicsState();
cs.transform(Matrix.getScaleInstance(rect.getWidth()/imgWidth*1.0f,rect.getHeight()/imgHeight*1.0f));
cs.drawImage(img, 0,0);
cs.restoreGraphicsState(); }
} // no need to set annotations and /P entry ByteArrayOutputStream baos = new ByteArrayOutputStream();
doc.save(baos);

// FileUtils.writeByteArrayToFile(new File("C:\Users\Administrator\Desktop\tem\pdfbox\sign-t.pdf"), baos.toByteArray());

return new ByteArrayInputStream(baos.toByteArray());

}

}

private PDRectangle createSignatureRectangle(PDDocument doc, Rectangle2D humanRect)
{
float x = (float) humanRect.getX();
float y = (float) humanRect.getY();
float width = (float) humanRect.getWidth();
float height = (float) humanRect.getHeight();
PDPage page = doc.getPage(0);
PDRectangle pageRect = page.getCropBox();
PDRectangle rect = new PDRectangle();
// signing should be at the same position regardless of page rotation.
switch (page.getRotation())
{
case 90:
rect.setLowerLeftY(x);
rect.setUpperRightY(x + width);
rect.setLowerLeftX(y);
rect.setUpperRightX(y + height);
break;
case 180:
rect.setUpperRightX(pageRect.getWidth() - x);
rect.setLowerLeftX(pageRect.getWidth() - x - width);
rect.setLowerLeftY(y);
rect.setUpperRightY(y + height);
break;
case 270:
rect.setLowerLeftY(pageRect.getHeight() - x - width);
rect.setUpperRightY(pageRect.getHeight() - x);
rect.setLowerLeftX(pageRect.getWidth() - y - height);
rect.setUpperRightX(pageRect.getWidth() - y);
break;
case 0:
default:
rect.setLowerLeftX(x);
rect.setUpperRightX(x + width);
rect.setLowerLeftY(pageRect.getHeight() - y - height);
rect.setUpperRightY(pageRect.getHeight() - y);
break;
}
return rect;
} private PDSignature findExistingSignature(PDAcroForm acroForm, String sigFieldName)
{
PDSignature signature = null;
PDSignatureField signatureField;
if (acroForm != null)
{
signatureField = (PDSignatureField) acroForm.getField(sigFieldName);
if (signatureField != null)
{
// retrieve signature dictionary
signature = signatureField.getSignature();
if (signature == null)
{
signature = new PDSignature();
// after solving PDFBOX-3524
// signatureField.setValue(signature)
// until then:
signatureField.getCOSObject().setItem(COSName.V, signature);
}
else
{
throw new IllegalStateException("The signature field " + sigFieldName + " is already signed.");
}
}
}
return signature;
}

}

`

基于Apache PDFBox的PDF数字签名的更多相关文章

  1. apache pdfbox

    转 http://www.blogjava.net/sxyx2008/archive/2010/07/23/326890.html 轻松使用apache pdfbox将pdf文件生成图 近期在项目中使 ...

  2. Apache PDFbox开发指南之PDF文档读取

    转载请注明来源:http://blog.csdn.net/loongshawn/article/details/51542309 相关文章: <Apache PDFbox开发指南之PDF文本内容 ...

  3. 使用Apache PDFBox实现拆分、合并PDF

    目录 使用Apache PDFBox实现拆分.合并PDF 问题背景 Apache PDFBox介绍 拆分PDF 合并PDF 拆分 + 合并 完整代码 参考: 使用Apache PDFBox实现拆分.合 ...

  4. Java 使用PDFBox提取PDF文件中的图片

    今天做PDF文件解析,遇到一个需求:提取文件中的图片并保存.使用的是流行的apache开源jar包pdfbox, 但还是遇到坑了,比如pdfbox版本太高或太低都不能用!!这个包竟然没有很好地做好兼容 ...

  5. java 用PDFBox 删除 PDF文件中的某一页

    依赖: <dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbox-app ...

  6. 使用pdfBox实现pdf转图片,解决中文方块乱码等问题

    一.引入依赖 <dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>fontbox ...

  7. APache PDFbox API使用(1)----简单介绍

    因为项目的须要.近期在学习APache  PDFbox API,Apache PDFbox API是Apache Java 开源社区中个一个项目,其受Apache 版权 V2的保护,其提供了以下的功能 ...

  8. pdfBox 读取pdf文件

    1.引入maven依赖 <dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pd ...

  9. pdfBox 解析 pdf文件

    Spting boot 项目 1.添加依赖 <dependency> <groupId>org.apache.pdfbox</groupId> <artifa ...

  10. java 库 pdfbox 将 pdf 文件转换成高清图片方法

    近期需要将 pdf 文件转成高清图片,使用库是 pdfbox.fontbox.可以使用 renderImageWithDPI 方法指定转换的清晰度,当然清晰度越高,转换需要的时间越长,转换出来的图片越 ...

随机推荐

  1. Kubernetes(K8S) Controller - Deployment 介绍

    什么是controller 实际存在的,管理和运行容器的对象 Pod 和 Controller 关系 Pod 是通过 Controller 实现应用的运维,比如伸缩.滚动升级等等 Pod 和 Cont ...

  2. MySQL 创建存储过程注意项

    MySQL server version for the  right syntax to use near 'IF' MySQL server version for the right synta ...

  3. JupyterLab 桌面版 !!!

    JupyterLab 是广受欢迎的 Jupyter Notebook「新」界面.它是一个交互式的开发环境,可用于 notebook.代码或数据,因此它的扩展性非常强. 用户可以使用它编写 notebo ...

  4. 巧用别名和 sh 脚本,adb 快速截图和录屏,提高你的效率

    前言 在平时开发过程中,我们经常需要截图和录制视频,尤其是客户端开发和测试. 可能有一些人的姿势是这样的.在电脑上开个模拟器,使用第三方工具后进行截图和录屏.还有一种最原始的方式,在手机上截图和录制视 ...

  5. Mysql--编译安装5.6版本

    1 下载编译工具 yum -y install cmake gcc gcc-c++ ncurses-devel autoconf 2 创建用户 目录 useradd -s /sbin/nologin ...

  6. 创建QUERY报表

    一.SQ02创建信息集 该事务代码用于查询需要的表,及表之间的关联关系 首先设置查询区域,标准区域中所建立的信息集仅在当前客户端使用,全局区域中建立的信息集可以跨client 创建信息集 选择基础表关 ...

  7. 采购订单创建、修改、审批增强ME21N/ME22N/ME28/ME29N

    一.采购订单创建修改增强 BADI:ME_PROCESS_PO_CUST 通过POST方法中的参数im_header,获取对应的数据 订单头 "----------------------- ...

  8. OS | 银行家算法C语言实现

    算法简介 银行家算法(Banker's Algorithm)是一个避免死锁( Deadlock)的著名算法,是由艾兹格·迪杰斯特拉在1965年为T.H.E系统设计的一种避免死锁产生的算法.它以银行借贷 ...

  9. ZOJ - 1610 区间修改+暴力单点查询

    一.内容 题意:给定[1,8000]区间,给定n组操作,每次将一段区间修改成某种颜色(对上一次颜色进行覆盖),最后问你能看到多少种颜色,且每种颜色有多少段. 二.思路 题目给定的区间是(x, y]左开 ...

  10. [Noip2012] 开车旅行 (倍增DP,难)

    题目链接:https://ac.nowcoder.com/acm/contest/1047/A Description 小 A 和小 B 决定利用假期外出旅行,他们将想去的城市从 1 到 N 编号,且 ...