Java使⽤Itext5.5.10进⾏pdf签章
啰嗦
说到PDF数字签名签章,这个其实也是数字证书信息安全的应⽤范畴,关于数字证书和数字签名,⽹上有很多解释说明,但讲解都多不够详细准确,这边推荐⼀篇⼤神的博⽂,讲解浅显易懂形象。刚⼊门CA⾏业的⼈,可以⼊门看看。
⾔归正传,正⽂开始
Itext包和 BC包
要⾃⼰实现PDF数字签章,是⼀件极其浩⼤的⼯程,难度很⼤(看看市⾯上多少公司是吃这⼀⾏的饭就知道了),好在java是个开源的世界,有很多开源项⽬。这⾥,咱们使⽤itext来实现⼀下pdf的数字签章(为什么挑itext,很⼤原因是,⾃⼰在做这⼀块的时候,⽹上对于itext的使⽤也有很多博⽂,不过,⼤多都是⽤的⽐较早期的itext,itext官⽹⽬前的版本的已经有了变化,⽹上普遍的做法都已经不适⽤了,还有⼀个原因,itext官⽹有官⽅教程,各个模块的样例,很⽅便)。
如果不知道在官⽹怎么下载jar包,这⾥附上我⾃⼰的下载地址⽅便⼤家, ,另外还需要密钥算法包 ,这个官⽹下载很简单,官⽹地址如下。
开始
咱们跟随样例,先来⼀个简单的签章。步骤如下:
⼀、准备⼀个pdf⽂档(貌似是废话)
⼆、准备⼀个图章图⽚(貌似也是废话)
三、准备⼀个keystore(只要是java keystore⽀持的格式都可以,例如.p12,如果没有,可以⽤bouncycastle⽣成⼀个,也很简单)。其实,Usbkey数字证书也是可以使⽤的,后边我再说这⼀块。
四、按照官⽹样例,写个.p12的签章代码。
代码
1、新建Java项⽬,导⼊itext包和 bc包
准备需要的资料
导⼊的包,应该有多余的包,我直接从项⽬中复制出来的
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import Certificate;
import X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import javax.swing.JOptionPane;
import DocumentException;
import Image;
import Rectangle;
import log.Logger;
import log.LoggerFactory;
import pdf.PdfReader;
import pdf.PdfSignatureAppearance;
import pdf.PdfSignatureAppearance.RenderingMode;
import pdf.PdfStamper;
import pdf.security.BouncyCastleDigest;
import pdf.security.CrlClient;
import pdf.security.DigestAlgorithms;
import pdf.security.ExternalDigest;
import pdf.security.ExternalSignature;
import pdf.security.MakeSignature;
import pdf.security.MakeSignature.CryptoStandard;
import pdf.security.PrivateKeySignature;
准备的资料
public static final String KEYSTORE = "F:\\ZzCert\\test.p12";
public static final char[] PASSWORD = "111111".toCharArray();//keystory密码
public static final String SRC = "F:\\test\\src.pdf";
public static final String DEST = "F:\\test\\signed_dest.pdf";
2、写个类,声明⼀个⽅法⽤来进⾏pdf签章
public void sign(String src //需要签章的pdf⽂件路径
, String dest // 签完章的pdf⽂件路径
, Certificate[] chain //证书链
, PrivateKey pk //签名私钥
, String digestAlgorithm //摘要算法名称,例如SHA-1
, String provider // 密钥算法提供者,可以为null
, CryptoStandard subfilter //数字签名格式,itext有2种
, String reason //签名的原因,显⽰在pdf签名属性中,随便填
, String location) //签名的地点,显⽰在pdf签名属性中,随便填
java源代码加密throws GeneralSecurityException, IOException, DocumentException {
//下边的步骤都是固定的,照着写就⾏了,没啥要解释的
// Creating the reader and the stamper,开始pdfreader
PdfReader reader = new PdfReader(src);
//⽬标⽂件输出流
FileOutputStream os = new FileOutputStream(dest);
//创建签章⼯具PdfStamper ,最后⼀个boolean参数
//false的话,pdf⽂件只允许被签名⼀次,多次签名,最后⼀次有效
//true的话,pdf可以被追加签名,验签⼯具可以识别出每次签名之后⽂档是否被修改 PdfStamper stamper = ateSignature(reader, os, '\0', null, true);
// 获取数字签章属性对象,设定数字签章的属性
PdfSignatureAppearance appearance = SignatureAppearance();
appearance.setReason(reason);
appearance.setLocation(location);
//设置签名的位置,页码,签名域名称,多次追加签名的时候,签名预名称不能⼀样
//签名的位置,是图章相对于pdf页⾯的位置坐标,原点为pdf页⾯左下⾓
//四个参数的分别是,图章左下⾓x,图章左下⾓y,图章右上⾓x,图章右上⾓y
appearance.setVisibleSignature(new Rectangle(200, 200, 300, 300), 1, "sig1");
//读取图章图⽚,这个image是itext包的image
Image image = Instance("F:\\test\\Dummy1.png");
appearance.setSignatureGraphic(image);
appearance.setCertificationLevel(PdfSignatureAppearance.NOT_CERTIFIED);
//设置图章的显⽰⽅式,如下选择的是只显⽰图章(还有其他的模式,可以图章和签名描述⼀同显⽰) appearance.setRenderingMode(RenderingMode.GRAPHIC);
// 这⾥的itext提供了2个⽤于签名的接⼝,可以⾃⼰实现,后边着重说这个实现
// 摘要算法
ExternalDigest digest = new BouncyCastleDigest();
// 签名算法
ExternalSignature signature = new PrivateKeySignature(pk, digestAlgorithm, null);
// 调⽤itext签名⽅法完成pdf签章
MakeSignature.signDetached(appearance, digest, signature, chain, null, null, null, 0, subfilter);
}
3、main⽅法中调⽤签章
调⽤代码很简单,如下
4、运⾏main⽅法就可以了。效果如下,⽤adobe reader可以看到图章,可以获取签名信息使⽤特殊签名算法
public static void main(String[] args) {
try {
//读取keystore ,获得私钥和证书链
KeyStore ks = KeyStore .getInstance ("PKCS12");
ks .load (new FileInputStream(KEYSTORE), PASSWORD);
String alias = (String)ks .aliases ().nextElement ();
PrivateKey pk = (PrivateKey) ks .getKey (alias, PASSWORD);
Certificate[] chain = ks .getCertificateChain (alias);
//new ⼀个上边⾃定义的⽅法对象,调⽤签名⽅法
MainWindow app = new MainWindow();
// app .sign (SRC, String .format (DEST, 1), chain, pk, DigestAlgorithms .SHA 1, provider .getName (), CryptoStandard .CMS , "Test 1", "Ghent");
// app .sign (SRC, String .format (DEST, 2), chain, pk, "SM3", provider .getName (), CryptoStandard .CADES , "Test 2", "Ghent");
app .sign (SRC, String .format (DEST, 3), chain, pk, DigestAlgorithms .SHA 1, null, CryptoStandard .CMS , "Test 3", "Ghent");
// app .sign (SRC, String .format (DEST, 4), chain, pk, DigestAlgorithms .RIPEMD 160, provider .getName (), CryptoStandard .CADES , "Test 4", "Ghent" } catch (Exception e) {
// TODO Auto-generated catch block
JOptionPane .showMessageDialog (null, e .getMessage ());
e .printStackTrace ();
}
}
上边的例⼦中,使⽤的是⽐较常见的签名算法-sha1withRsa,itext⽀持国际流⾏的⼤部分签名算法。
当然itext也⽀持特殊的签名算法,例如国密,为什么itext不把国密算法也封装进jar包呢,⼀个原因是国密并不是国际通⽤标准,⼆是即便把国密封装进jar包,进⾏完签名后,⼀般的pdf阅读器也是⽆法验签的,因为adobe 的pdf标准没有国密算法。
即便如此,我们依然可以⾃⼰把国密算法加到itext签章中,只不过阅读器⽆法验签就对了。主要⽤的就是上边例⼦中的2个接⼝。
// 摘要算法
ExternalDigest digest ;
// 签名算法
ExternalSignature signature ;
我们可以通过⾃⼰实现这2个接⼝,来添加国密算法。
看看这连个接⼝的源码,都很简单,digest接⼝返回MessageDigest,实现的时候,直接new ⼀个Mes
sageDigest,然后实现MessageDigest的抽象⽅法,把⾃⼰实现的SM3算法加进去就可以了(SM3withSM2按照国密的标准,sm3要加预处理,具体怎么做,百度很多,这⾥不多说)
signature 接⼝3个抽象⽅法,分别返回摘要算法名称(例如SM3 或者SHA1等),签名算法中使⽤的加密算法名称(例如SM2 或者RSA 等), 第三个抽象⽅法sign就是具体的签名算法,传⼊的参数message是签名原⽂,返回值是签名结果,针对国密算法来说,就可以把⾃⼰实现好的 sm3withsm2签名算法 写进去。
另外,需要注意,实现接⼝后,运⾏main会提⽰错误,原因是 ⾃⼰实现的国密接⼝的OID并没有加⼊到itext源码中,可以根据错误提⽰,到需要加⼊oid的地⽅,直接把算法oid写进去后 itext就可以认到我们⾃⼰实现的算法了。⼤致有2个地⽅要加,⼀个是摘要算法的oid,⼀个是签名算法的oid
package pdf.security;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
/**
*
* @author psoares
*/
public interface ExternalDigest {
public MessageDigest getMessageDigest(String hashAlgorithm) throws GeneralSecurityException;
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论