当前位置:   article > 正文

基于Apache PDFBox的PDF数字签名_pdfbox 电子签章

pdfbox 电子签章

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

  1. package org.resrun.sdk.pdfbox;
  2. import org.resrun.sdk.pdfbox.vo.AssinaturaModel;
  3. import org.apache.pdfbox.Loader;
  4. import org.apache.pdfbox.cos.COSName;
  5. import org.apache.pdfbox.io.IOUtils;
  6. import org.apache.pdfbox.pdmodel.PDDocument;
  7. import org.apache.pdfbox.pdmodel.PDPage;
  8. import org.apache.pdfbox.pdmodel.PDPageContentStream;
  9. import org.apache.pdfbox.pdmodel.PDResources;
  10. import org.apache.pdfbox.pdmodel.common.PDRectangle;
  11. import org.apache.pdfbox.pdmodel.common.PDStream;
  12. import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
  13. import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
  14. import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
  15. import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
  16. import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
  17. import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
  18. import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureOptions;
  19. import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
  20. import org.apache.pdfbox.pdmodel.interactive.form.PDField;
  21. import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;
  22. import org.apache.pdfbox.util.Matrix;
  23. import java.awt.geom.AffineTransform;
  24. import java.awt.geom.Rectangle2D;
  25. import java.io.ByteArrayInputStream;
  26. import java.io.ByteArrayOutputStream;
  27. import java.io.IOException;
  28. import java.io.InputStream;
  29. import java.security.KeyStoreException;
  30. import java.security.NoSuchAlgorithmException;
  31. import java.security.UnrecoverableKeyException;
  32. import java.security.cert.CertificateException;
  33. import java.util.Calendar;
  34. import java.util.List;
  35. public class AssinaturaPDF extends SignatureBase {
  36. private SignatureOptions signatureOptions;
  37. private AssinaturaModel assinatura;
  38. public AssinaturaPDF(AssinaturaModel assinaturaModel) throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, IOException, CertificateException {
  39. super(assinaturaModel.getCertInfo());
  40. this.assinatura = assinaturaModel;
  41. if (assinaturaModel.getTsa() != null && !assinaturaModel.getTsa().equals("")) {
  42. setTsaUrl(assinaturaModel.getTsa());
  43. }
  44. }
  45. public byte [] assina() throws Exception {
  46. if (assinatura.getPdf() == null || assinatura.getPdf().length == 0) {
  47. throw new Exception("pdf file null");
  48. }
  49. try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
  50. PDDocument doc = Loader.loadPDF(assinatura.getPdf())) {
  51. if (doc.isEncrypted()) {
  52. try {
  53. doc.setAllSecurityToBeRemoved(true);
  54. } catch (Exception e) {
  55. throw new Exception("pdf passwd error", e);
  56. }
  57. }
  58. int accessPermissions = SigUtils.getMDPPermission(doc);
  59. if (accessPermissions == 1)
  60. {
  61. throw new IllegalStateException("No changes to the document are permitted due to DocMDP transform parameters dictionary");
  62. }
  63. PDSignature signature = null;
  64. PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm(null);
  65. PDRectangle rect = null;
  66. // TODO 签名域的位置 可能需要再计算
  67. Rectangle2D humanRect = new Rectangle2D.Float(assinatura.getPosition().getParseX(), assinatura.getPosition().getParseY(),
  68. assinatura.getPosition().getSignWidthFloat(), assinatura.getPosition().getSignHeightFloat());
  69. // sign a PDF with an existing empty signature, as created by the CreateEmptySignatureForm example.
  70. if (acroForm != null)
  71. {
  72. signature = findExistingSignature(acroForm, assinatura.getSignatureKey());
  73. if (signature != null)
  74. {
  75. rect = acroForm.getField(assinatura.getSignatureKey()).getWidgets().get(0).getRectangle();
  76. }
  77. }
  78. if (signature == null)
  79. {
  80. // create signature dictionary
  81. signature = new PDSignature();
  82. }
  83. if (rect == null)
  84. {
  85. rect = createSignatureRectangle(doc, humanRect);
  86. }
  87. if (doc.getVersion() >= 1.5f && accessPermissions == 0)
  88. {
  89. SigUtils.setMDPPermission(doc, signature, 2);
  90. }
  91. if (acroForm != null && acroForm.getNeedAppearances())
  92. {
  93. // PDFBOX-3738 NeedAppearances true results in visible signature becoming invisible
  94. // with Adobe Reader
  95. if (acroForm.getFields().isEmpty())
  96. {
  97. // we can safely delete it if there are no fields
  98. acroForm.getCOSObject().removeItem(COSName.NEED_APPEARANCES);
  99. // note that if you've set MDP permissions, the removal of this item
  100. // may result in Adobe Reader claiming that the document has been changed.
  101. // and/or that field content won't be displayed properly.
  102. // ==> decide what you prefer and adjust your code accordingly.
  103. }
  104. else
  105. {
  106. System.out.println("/NeedAppearances is set, signature may be ignored by Adobe Reader");
  107. }
  108. }
  109. // default filter
  110. signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
  111. // subfilter for basic and PAdES Part 2 signatures
  112. signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
  113. signature.setName("Name");
  114. signature.setLocation("Location");
  115. signature.setReason("Reason");
  116. // the signing date, needed for valid signature
  117. signature.setSignDate(Calendar.getInstance());
  118. signatureOptions = new SignatureOptions();
  119. signatureOptions.setVisualSignature(createVisualSignatureTemplate(doc, 0, rect, signature));
  120. signatureOptions.setPage(assinatura.getPosition().getPage());
  121. // signatureOptions.setPreferredSignatureSize(SignatureOptions.DEFAULT_SIGNATURE_SIZE * 2);
  122. doc.addSignature(signature, this, signatureOptions);
  123. // doc.saveIncremental(bos);
  124. doc.saveIncremental(bos);
  125. IOUtils.closeQuietly(signatureOptions);
  126. doc.close();
  127. return bos.toByteArray();
  128. // return null;
  129. }
  130. }
  131. private InputStream createVisualSignatureTemplate(PDDocument srcDoc, int pageNum,
  132. PDRectangle rect, PDSignature signature) throws IOException
  133. {
  134. try (PDDocument doc = new PDDocument())
  135. {
  136. PDPage page = new PDPage(srcDoc.getPage(pageNum).getMediaBox());
  137. doc.addPage(page);
  138. PDAcroForm acroForm = new PDAcroForm(doc);
  139. doc.getDocumentCatalog().setAcroForm(acroForm);
  140. PDSignatureField signatureField = new PDSignatureField(acroForm);
  141. PDAnnotationWidget widget = signatureField.getWidgets().get(0);
  142. List<PDField> acroFormFields = acroForm.getFields();
  143. acroForm.setSignaturesExist(true);
  144. acroForm.setAppendOnly(true);
  145. acroForm.getCOSObject().setDirect(true);
  146. acroFormFields.add(signatureField);
  147. widget.setRectangle(rect);
  148. // from PDVisualSigBuilder.createHolderForm()
  149. PDStream stream = new PDStream(doc);
  150. PDFormXObject form = new PDFormXObject(stream);
  151. PDResources res = new PDResources();
  152. form.setResources(res);
  153. form.setFormType(1);
  154. PDRectangle bbox = new PDRectangle(rect.getWidth(), rect.getHeight());
  155. float height = bbox.getHeight();
  156. Matrix initialScale = null;
  157. switch (srcDoc.getPage(pageNum).getRotation())
  158. {
  159. case 90:
  160. form.setMatrix(AffineTransform.getQuadrantRotateInstance(1));
  161. initialScale = Matrix.getScaleInstance(bbox.getWidth() / bbox.getHeight(), bbox.getHeight() / bbox.getWidth());
  162. height = bbox.getWidth();
  163. break;
  164. case 180:
  165. form.setMatrix(AffineTransform.getQuadrantRotateInstance(2));
  166. break;
  167. case 270:
  168. form.setMatrix(AffineTransform.getQuadrantRotateInstance(3));
  169. initialScale = Matrix.getScaleInstance(bbox.getWidth() / bbox.getHeight(), bbox.getHeight() / bbox.getWidth());
  170. height = bbox.getWidth();
  171. break;
  172. case 0:
  173. default:
  174. break;
  175. }
  176. form.setBBox(bbox);
  177. // from PDVisualSigBuilder.createAppearanceDictionary()
  178. PDAppearanceDictionary appearance = new PDAppearanceDictionary();
  179. appearance.getCOSObject().setDirect(true);
  180. PDAppearanceStream appearanceStream = new PDAppearanceStream(form.getCOSObject());
  181. appearance.setNormalAppearance(appearanceStream);
  182. widget.setAppearance(appearance);
  183. try (PDPageContentStream cs = new PDPageContentStream(doc, appearanceStream))
  184. {
  185. // for 90° and 270° scale ratio of width / height
  186. // not really sure about this
  187. // why does scale have no effect when done in the form matrix???
  188. if (initialScale != null)
  189. {
  190. cs.transform(initialScale);
  191. }
  192. // show background (just for debugging, to see the rect size + position)
  193. // cs.setNonStrokingColor(Color.yellow);
  194. // cs.addRect(-5000, -5000, 10000, 10000);
  195. cs.fill();
  196. if (assinatura.getSignatureImage() != null)
  197. {
  198. PDImageXObject img = PDImageXObject.createFromByteArray(doc,assinatura.getSignatureImage(),"test");
  199. int imgHeight = img.getHeight();
  200. int imgWidth = img.getWidth();
  201. cs.saveGraphicsState();
  202. cs.transform(Matrix.getScaleInstance(rect.getWidth()/imgWidth*1.0f,rect.getHeight()/imgHeight*1.0f));
  203. cs.drawImage(img, 0,0);
  204. cs.restoreGraphicsState();
  205. }
  206. }
  207. // no need to set annotations and /P entry
  208. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  209. doc.save(baos);
  210. // FileUtils.writeByteArrayToFile(new File("C:\\Users\\Administrator\\Desktop\\tem\\pdfbox\\sign-t.pdf"), baos.toByteArray());
  211. return new ByteArrayInputStream(baos.toByteArray());
  212. }
  213. }
  214. private PDRectangle createSignatureRectangle(PDDocument doc, Rectangle2D humanRect)
  215. {
  216. float x = (float) humanRect.getX();
  217. float y = (float) humanRect.getY();
  218. float width = (float) humanRect.getWidth();
  219. float height = (float) humanRect.getHeight();
  220. PDPage page = doc.getPage(0);
  221. PDRectangle pageRect = page.getCropBox();
  222. PDRectangle rect = new PDRectangle();
  223. // signing should be at the same position regardless of page rotation.
  224. switch (page.getRotation())
  225. {
  226. case 90:
  227. rect.setLowerLeftY(x);
  228. rect.setUpperRightY(x + width);
  229. rect.setLowerLeftX(y);
  230. rect.setUpperRightX(y + height);
  231. break;
  232. case 180:
  233. rect.setUpperRightX(pageRect.getWidth() - x);
  234. rect.setLowerLeftX(pageRect.getWidth() - x - width);
  235. rect.setLowerLeftY(y);
  236. rect.setUpperRightY(y + height);
  237. break;
  238. case 270:
  239. rect.setLowerLeftY(pageRect.getHeight() - x - width);
  240. rect.setUpperRightY(pageRect.getHeight() - x);
  241. rect.setLowerLeftX(pageRect.getWidth() - y - height);
  242. rect.setUpperRightX(pageRect.getWidth() - y);
  243. break;
  244. case 0:
  245. default:
  246. rect.setLowerLeftX(x);
  247. rect.setUpperRightX(x + width);
  248. rect.setLowerLeftY(pageRect.getHeight() - y - height);
  249. rect.setUpperRightY(pageRect.getHeight() - y);
  250. break;
  251. }
  252. return rect;
  253. }
  254. private PDSignature findExistingSignature(PDAcroForm acroForm, String sigFieldName)
  255. {
  256. PDSignature signature = null;
  257. PDSignatureField signatureField;
  258. if (acroForm != null)
  259. {
  260. signatureField = (PDSignatureField) acroForm.getField(sigFieldName);
  261. if (signatureField != null)
  262. {
  263. // retrieve signature dictionary
  264. signature = signatureField.getSignature();
  265. if (signature == null)
  266. {
  267. signature = new PDSignature();
  268. // after solving PDFBOX-3524
  269. // signatureField.setValue(signature)
  270. // until then:
  271. signatureField.getCOSObject().setItem(COSName.V, signature);
  272. }
  273. else
  274. {
  275. throw new IllegalStateException("The signature field " + sigFieldName + " is already signed.");
  276. }
  277. }
  278. }
  279. return signature;
  280. }
  281. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/539045
推荐阅读
相关标签
  

闽ICP备14008679号