参考文章:http://www.zzvips.com/article/195911.html

数据类型

直接对象

八种直接对象类型 约束 示例
Boolean Objects 值为true、false
Numeric Objects 包含整型和浮点型 12
0.01
String Objects 由一系列0-255之间的字节组成,总长度不能超过65535
文字字符用()括起来
16进制数据用<>括起来
字符串对象必须被完整的读入
(Brillig)
<0123456789ABCDEF>
Name Objects 由一个前导/ 和后面一系列字符组成,最大长度为127。
Name是一个原子符号且具有唯一性,大写字符与小字符被当为不同字符
/Name1
Array Objects 用[]包含的一组对象,可是以任何pdf对象(包括array)
数组元素个数不能超过8191
[ 549 3.14 false ( Ralph ) /SomeName ]
Dictionary Objects 用<< >>包含的若干组条目
每组条目都是由key和value组成,其中key必须是name对象,Value可以是任何pdf的对象
一个dictionary内的key唯一
Stream Objects 流的数据被包含在stream 和endstream对中
流的数据是0 或多个字节
PDF 应用程序可以增量读入流对象
流对象的长度是无限制的
Null Objects 用null表示,代表空。如果一个key的值为null,则这个key可以被忽略

间接对象

任何对象都可以被标注为一个间接对象,在文档的任何地方都可以引用对象

间接对象的定义方法是:对象号码,然后后代号码,再跟一对关键字obj 和endobj
备注:后台号码指更新次数,一般都是0
示例:它的对象号码是12,后代号码是0,值是Brilling

间接对象的引用方法是:对象号码,加后代号码、再加关键字R
例如:12 0 R

文档结构

一个pdf文档分为4个部分:
1. 文件头,指明该文件所遵从的pdf规范的版本,它出现在pdf文档的第一行
2. 文件体,pdf文件的主要部分,由一系列的对象组成
3. 交叉引用表,为了能对间接对象进行随机存取而高效的一个间接对象的地址索引表
4. 文件尾,声明了交叉引用表的地址,指明了文件体的根对象,从而能够找到pdf文件中各个对象的位置,实现随机访问。

即:文件尾是解析文档的入口


用记事本工具打开PDF,示例:

文件头

文件尾

从文件尾可以看出:
文件根对象:/Root 1 0 R,对象1里面定义了页码Pages、语言及其他文档信息;

PDF签章文件结构

PDF签章:将图片放入PDF,PDF+图片一起作为原文,进行签名

PDF签章格式:
adbe.pkcs7.detached(P7不带内容/原文)
adbe.pkcs7.sha1(P7带内容。先对PDF数据做SHA1,再把SHA1数据作为P7内容,相当于做了2次摘要)
adbe.x509.rsa_sha1(数字证书+P1签名):实际场景俗称裸签,哈希算法不一定是SHA1,有可能是SHA256。遇到了一个验滴滴发票的需求,就是用的SHA256做哈希,修改了itext源码来支持验签章的。
ETSI.CAdES.detached(CAdES不带内容)

一次签章

/ByteRange [offset1,len1,offset2,len2],表示PDF的两部分原文的位置。
示例:[0,840,960,240],0和960是偏移量,840和240是长度。
0-840:是签名原文的第一部分,这一部分会放一个图片,和原数据一起成为签名原文;
960-1200:是签名原文的第二部分;
840-960:是需要预留用于放签名数据的,具体预留多少,可以无限大,缺点就是最终文件会很大;也可以先对第一部分签名,就会得出大致的长度,在此基础上,再添加一些长度作为预留。


多次签章

PDF签章代码示例

需要用到itextpdf和BouncyCastleProvider的jar包:
itextpdf-5.5.5-with-asian.jar
bcpkix-jdk15on-1.60.jar
bcprov-jdk15on-1.60.jar

package cn.com.gs.common.util.pdf;import cn.com.gs.common.define.Constants;
import cn.com.gs.common.exception.NetGSRuntimeException;
import cn.com.gs.common.util.StringUtil;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import com.itextpdf.text.pdf.security.*;
import org.bouncycastle.jce.provider.BouncyCastleProvider;import java.io.ByteArrayOutputStream;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.util.ArrayList;/*** 参考文章:https://blog.csdn.net/tomatocc/article/details/80762507* @author Administator*/
public class PdfStampUtil {static {if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {Security.addProvider(new BouncyCastleProvider());}}/*** PDF添加图片并签名* @param pdfData pdf数据* @param photoData 图片数据* @param pageNumber 页码* @param x x坐标* @param y y坐标* @param chain 证书链* @param privateKey 私钥* @param hashAlg 摘要算法* @return* @throws Exception*/public byte[] sign(byte[] pdfData, byte[] photoData, int pageNumber, float x, float y,Certificate[] chain, PrivateKey privateKey, String hashAlg) throws Exception {PdfReader reader = new PdfReader(pdfData);/** 创建签章工具PdfStamper,* 第二个参数是输出流,签完的文件放在这个输出流,我们获取* 最后一个boolean参数是否允许被追加签名* false的话,pdf文件只允许被签名一次,多次签名,最后一次有效* true的话,pdf可以被追加签名,验签工具可以识别出每次签名之后文档是否被修改*/ByteArrayOutputStream outputStream = new ByteArrayOutputStream();PdfStamper stamper = PdfStamper.createSignature(reader, outputStream, '\0', null, true);// 1.设置PdfSignatureAppearancePdfSignatureAppearance sap = stamper.getSignatureAppearance();// 1.1设置图章图片Image image = Image.getInstance(photoData);sap.setSignatureGraphic(image);// 1.2设置图章的显示方式,这里是GRAPHIC只显示图章(还有其他的模式,可以图章和签名描述一同显示),不设置默认是展示描述sap.setRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC);// 1.3设置图章位置,页码,签名域名称,多次追加签名的时候,签名预名称不能一样 图片大小受表单域大小影响(过小导致压缩)// 签名的坐标,是图章相对于pdf页面的位置坐标,原点为pdf页面左下角// 四个参数的分别是,图章左下角x,图章左下角y,图章右上角x,图章右上角yfloat imageWidth = image.getWidth() * 72f / Constants.DPI;float imageHeight = image.getHeight() * 72f / Constants.DPI;float ux = x + imageWidth;float uy = y + imageHeight;sap.setVisibleSignature(new Rectangle(x, y, ux, uy), pageNumber, StringUtil.genDigitRandom(10));// 2.摘要算法ExternalDigest digest = new BouncyCastleDigest();// 3.签名算法ExternalSignature signature = new PrivateKeySignature(privateKey, hashAlg, null);// 签章MakeSignature.signDetached(sap, digest, signature, chain,null, null, null, 0, MakeSignature.CryptoStandard.CADES);stamper.close();reader.close();return outputStream.toByteArray();}/*** PDF验签名* @param pdfData* @return* @throws Exception*/public boolean verifySign(byte[] pdfData) throws Exception {PdfReader reader = new PdfReader(pdfData);AcroFields fields = reader.getAcroFields();ArrayList<String> names = fields.getSignatureNames();for (int i = 0, size = names.size(); i < size; i++) {String signName = (String) names.get(i);PdfDictionary dictionary = fields.getSignatureDictionary(signName);PdfName sub = dictionary.getAsName(PdfName.SUBFILTER);if (PdfName.ETSI_CADES_DETACHED.equals(sub)) {PdfPKCS7 pkcs7 = fields.verifySignature(signName);return pkcs7.verify();} else {throw new NetGSRuntimeException("暂不支持的SubFilter类型:" + sub);}}return false;}}

测试

public class UtilTest {/*** pdf签章* @throws Exception*/@Testpublic void pdfStampTest() throws Exception {String password = "11111111";String pfxPath = Constants.FILE_PATH + "/key/rsa/rsapfx3des-sha1.pfx";PdfStampUtil pdfUtil = new PdfStampUtil();// 读取keystore ,获得私钥KeyStore ks = KeyStore.getInstance("PKCS12");ks.load(new FileInputStream(pfxPath), password.toCharArray());String alias = ks.aliases().nextElement();PrivateKey pk = (PrivateKey) ks.getKey(alias, password.toCharArray());// 得到证书链Certificate[] chain = ks.getCertificateChain(alias);//签章byte[] pdfData = FileUtil.getFile(Constants.FILE_PATH + "2页.pdf");byte[] photoData = FileUtil.getFile(Constants.FILE_PATH + "999.png");byte[] signedData = pdfUtil.sign(pdfData, photoData,1,100, 100, chain, pk, DigestAlgorithms.SHA1);FileUtil.storeFile(Constants.FILE_OUT_PATH + "stamp.pdf", signedData);System.out.println("签章成功,文件存储路径为:" + Constants.FILE_OUT_PATH + "stamp.pdf");}@Testpublic void pdfVerifyTest() throws Exception {byte[] pdfData = FileUtil.getFile(Constants.FILE_OUT_PATH + "stamp.pdf");PdfStampUtil pdfUtil = new PdfStampUtil();boolean verify = pdfUtil.verifySign(pdfData);System.out.println("验签结果:" + verify);}
}

PDF示例

解析一个签章文件


RSA签章

/Contents下面的真正内容是一个DER编码的PKCS#7数据对象


SM2签章

/Contents下面的真正内容是符合签章规范的数据,例如38540规范下的签章:

代码示例

 @Testpublic void getSealFromPDFStamp() throws Exception {String pdfStampPath = "f:/temp/stamp.pdf";String stampPath = "f:/temp/1.stamp";byte[] pdfData = FileUtil.getFile(pdfStampPath);PdfReader reader = null;try {reader = new PdfReader(pdfData);AcroFields af = reader.getAcroFields();ArrayList<String> names = af.getSignatureNames();// 获取每一个签名域的 签名值for (String name : names) {PdfDictionary dictionary = af.getSignatureDictionary(name);byte[] bytes = dictionary.getAsString(PdfName.CONTENTS).getBytes();String hexContents = HexUtil.byte2Hex(bytes);// 去除尾部填充的0while (hexContents.endsWith("00"))hexContents = hexContents.substring(0, hexContents.length() - 2);FileUtil.storeFile(stampPath, HexUtil.hex2Byte(hexContents));}} catch (Exception e) {throw e;} finally {try {if (reader != null)reader.close();} catch (Exception e) {}}}

PDF文件结构及签章相关推荐

  1. 【itext学习之路】--5.对pdf进行盖章/签章/数字签名

    来源:[itext学习之路]-------(第五篇)对pdf进行盖章/签章/数字签名_tomatocc的博客-CSDN博客_itext 数字签名 在上一篇文章中,我们学习了使用itext对pdf增加图 ...

  2. 使用Java对PDF进行电子签章

    使用Java对PDF进行电子签章 开始之前 前期准备 开始 生成keystore证书 来张材料全家福 编码 项目结构 签署工具类 开始之前 公司近期做的项目用到了电子签章(给PDF盖章签名),这过程真 ...

  3. java操作pdf制作电子签章

    #java操作pdf制作电子签章 ##电子签章简介 电子签章,与我们所使用的数字证书一样,是用来做为身份验证的一种手段,泛指所有以电子形式存在,依附在电子文件并与其逻辑关联,可用以辨识电子文件签署者身 ...

  4. HTML页面实现可编辑,保存,并生成PDF,完成签章。

    原本是应该用pageOffice实现在线打开word的编辑,保存,转换成PDF,并完成签章,由于公司预算不够,pageOffice组件不能购买,所以只能另想他法. 需要编辑的word文档的模板是固定的 ...

  5. 在嵌入在html中的pdf电子签章,一种网页PDF中电子签章定位方法与流程

    本发明属于一种电子签章技术领域,具体涉及一种网页PDF中电子签章定位方法. 背景技术: 国密电子签章中的套章方法操作复杂,无法对电子文档中一次性加盖多个印章的,大大降低了盖章效率,尤其是无法在所有需要 ...

  6. java使用poi、itextpdf将word、ppt转为pdf文件,并对pdf文件加盖签章

    1 环境及背景 SpringBoot项目,使用poi.itextpdf将excel.ppt.word文件转为pdf,并对pdf文件进行签章: 对Excel文件加图片水印,并加密设置为只读. 下面的方法 ...

  7. PDF文件结构(二) 逻辑结构

    PDF文件结构(二) ----逻辑结构 作者:bobob 邮件:zxbbobob@hotmail.com 原文:http://blog.csdn.net/bobob/article/details/4 ...

  8. 结合编辑器和PDFXplorer工具查看PDF文件结构

    首先用编辑器打开PDF,可以看到如下结构 1.寻找文件结构入口 /Root k-value形式,/Root 98 0 R/ k是Root value是98 0 R 98 0 R 代表被引用,指向的对象 ...

  9. Java对PDF进行电子签章CA签名认证

    什么是CA? CA是认证中心的英文Certification Authority的缩写.它为电子商务环境中各个实体颁发数字证书,以证明各实体身份的真实性,并负责在交易中检验和管理证书:它是电子商务和网 ...

最新文章

  1. 流数据分析平台Storm简介
  2. BZOJ 3524主席树裸题 (雾)
  3. 第07章:MongoDB-CRUD操作--文档--创建
  4. IDC:PC时代将结束 微软谷歌是最大输家和赢家
  5. 算法笔记_231:网格中移动字母(Java)
  6. 一位父亲和一位母亲讲述孩子的成长故事--《粗养的智慧:李聃的普林斯顿之路》和《我的儿子马友友》阅读摘录...
  7. 复杂多目录的Makefile模板及示例-转
  8. Android Vendor Test Suite (VTS) 作用及测试方法
  9. 安卓计步器是如何实现计步的
  10. wifi射频设计指南
  11. 整数规划--指派问题
  12. html中加入emjio表情,jqueryemoji表情插件
  13. 【新浪云共享型MYSQL】Navicat连接新浪云共享型MYSQL附JAVA/PHP配置文件)
  14. tr命令解析_学习笔记
  15. php 游戏开发swoole,《基于 Swoole 的对战游戏实践》开课啦
  16. eclipse快捷键(增加一些4连组合快捷键)
  17. 多少秒算长镜头_长镜头的作用
  18. Android 学习论坛博客及网站推荐
  19. 阿里云开放平台微前端方案的沙箱实现
  20. 自己留着用的 .net 图片水印 方法

热门文章

  1. mysql unsupported data type._mysql unsupported operand type(s) for %: 'tuple' and 'tuple'
  2. ​TIKTOK的MCN机构入驻教程
  3. jitter 如何优化网络_爱帮网络推广|如何选择符合企业要求的SEO优化公司?
  4. UIP协议栈笔记·二
  5. 重读《C primer plus》(十)
  6. 《封号码罗》python爬虫之文字点选型验证码破解一次仅需2分5厘人民币(十)
  7. 消费平板售后镜像一键刷机操作教程
  8. 大数据数据仓库建设方案
  9. php 二进制字符串转图片,PHP二进制与字符串之间的相互转换_PHP教程
  10. 这2万字的前端基础知识查漏补缺,请你收藏好