动漫网站怎么做的,成都网站优化seo,网站建设开发平台,个人网站要备案吗来源#xff1a;Java使用Itext5.5.10进行pdf签章_liumengya007007的博客-CSDN博客_itext 签章
啰嗦
说到PDF数字签名签章#xff0c;这个其实也是数字证书信息安全的应用范畴#xff0c;关于数字证书和数字签名#xff0c;网上有很多解释说明#xff0c;但讲解都多不够详…来源Java使用Itext5.5.10进行pdf签章_liumengya007007的博客-CSDN博客_itext 签章
啰嗦
说到PDF数字签名签章这个其实也是数字证书信息安全的应用范畴关于数字证书和数字签名网上有很多解释说明但讲解都多不够详细准确这边推荐一篇大神的博文讲解浅显易懂形象数字证书 数字签名 数据加密。刚入门CA行业的人可以入门看看。 言归正传正文开始
Itext包 和 BC包
要自己实现PDF数字签章是一件极其浩大的工程难度很大看看市面上多少公司是吃这一行的饭就知道了好在java是个开源的世界有很多开源项目。这里咱们使用itext来实现一下pdf的数字签章为什么挑itext很大原因是自己在做这一块的时候网上对于itext的使用也有很多博文不过大多都是用的比较早期的itextitext官网目前的版本的已经有了变化网上普遍的做法都已经不适用了还有一个原因itext官网有官方教程各个模块的样例很方便。 如果不知道在官网怎么下载jar包这里附上我自己的下载地址方便大家 itextpdf-5.5.10 源码、jar包、doc文档 另外还需要密钥算法包bouncycastle.org 这个官网下载很简单官网地址如下bouncycastle.org官网。
开始
咱们跟随样例先来一个简单的签章。步骤如下 一、准备一个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 java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import javax.swing.JOptionPane;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.log.Logger;
import com.itextpdf.text.log.LoggerFactory;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfSignatureAppearance;
import com.itextpdf.text.pdf.PdfSignatureAppearance.RenderingMode;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.security.BouncyCastleDigest;
import com.itextpdf.text.pdf.security.CrlClient;
import com.itextpdf.text.pdf.security.DigestAlgorithms;
import com.itextpdf.text.pdf.security.ExternalDigest;
import com.itextpdf.text.pdf.security.ExternalSignature;
import com.itextpdf.text.pdf.security.MakeSignature;
import com.itextpdf.text.pdf.security.MakeSignature.CryptoStandard;
import com.itextpdf.text.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签名属性中随便填throws GeneralSecurityException, IOException, DocumentException {//下边的步骤都是固定的照着写就行了没啥要解释的// Creating the reader and the stamper开始pdfreaderPdfReader reader new PdfReader(src);//目标文件输出流FileOutputStream os new FileOutputStream(dest);//创建签章工具PdfStamper 最后一个boolean参数 //false的话pdf文件只允许被签名一次多次签名最后一次有效//true的话pdf可以被追加签名验签工具可以识别出每次签名之后文档是否被修改PdfStamper stamper PdfStamper.createSignature(reader, os, \0, null, true);// 获取数字签章属性对象设定数字签章的属性PdfSignatureAppearance appearance stamper.getSignatureAppearance();appearance.setReason(reason);appearance.setLocation(location);//设置签名的位置页码签名域名称多次追加签名的时候签名预名称不能一样//签名的位置是图章相对于pdf页面的位置坐标原点为pdf页面左下角//四个参数的分别是图章左下角x图章左下角y图章右上角x图章右上角yappearance.setVisibleSignature(new Rectangle(200, 200, 300, 300), 1, sig1);//读取图章图片这个image是itext包的imageImage image Image.getInstance(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方法中调用签章 调用代码很简单如下
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.SHA1, 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.SHA1, null, CryptoStandard.CMS, Test 3, Ghent);
// app.sign(SRC, String.format(DEST, 4), chain, pk, DigestAlgorithms.RIPEMD160, provider.getName(), CryptoStandard.CADES, Test 4, Ghent);} catch (Exception e) {// TODO Auto-generated catch blockJOptionPane.showMessageDialog(null, e.getMessage());e.printStackTrace();} } 4、运行main方法就可以了。效果如下用adobe reader可以看到图章可以获取签名信息 使用特殊签名算法
上边的例子中使用的是比较常见的签名算法-sha1withRsaitext支持国际流行的大部分签名算法。 当然itext也支持特殊的签名算法例如国密为什么itext不把国密算法也封装进jar包呢一个原因是国密并不是国际通用标准二是即便把国密封装进jar包进行完签名后一般的pdf阅读器也是无法验签的因为adobe 的pdf标准没有国密算法。 即便如此我们依然可以自己把国密算法加到itext签章中只不过阅读器无法验签就对了。主要用的就是上边例子中的2个接口。 // 摘要算法ExternalDigest digest ;// 签名算法ExternalSignature signature ;
我们可以通过自己实现这2个接口来添加国密算法。 看看这连个接口的源码都很简单digest接口返回MessageDigest实现的时候直接new 一个MessageDigest然后实现MessageDigest的抽象方法把自己实现的SM3算法加进去就可以了SM3withSM2按照国密的标准sm3要加预处理具体怎么做百度很多这里不多说 signature 接口3个抽象方法分别返回摘要算法名称例如SM3 或者SHA1等签名算法中使用的加密算法名称例如SM2 或者RSA等 第三个抽象方法sign就是具体的签名算法传入的参数message是签名原文返回值是签名结果针对国密算法来说就可以把自己实现好的 sm3withsm2签名算法 写进去。 另外需要注意实现接口后运行main会提示错误原因是 自己实现的国密接口的OID并没有加入到itext源码中可以根据错误提示找到需要加入oid的地方直接把算法oid写进去后 itext就可以认到我们自己实现的算法了。大致有2个地方要加一个是摘要算法的oid一个是签名算法的oid
package com.itextpdf.text.pdf.security;import java.security.GeneralSecurityException;
import java.security.MessageDigest;/**** author psoares*/
public interface ExternalDigest {public MessageDigest getMessageDigest(String hashAlgorithm) throws GeneralSecurityException;
}
package com.itextpdf.text.pdf.security;import java.security.GeneralSecurityException;/*** Interface that needs to be implemented to do the actual signing.* For instance: youll have to implement this interface if you want* to sign a PDF using a smart card.* author Paulo Soares*/
public interface ExternalSignature {/*** Returns the hash algorithm.* return the hash algorithm (e.g. SHA-1, SHA-256,...)*/public String getHashAlgorithm();/*** Returns the encryption algorithm used for signing.* return the encryption algorithm (RSA or DSA)*/public String getEncryptionAlgorithm();/*** Signs it using the encryption algorithm in combination with* the digest algorithm.* param message the message you want to be hashed and signed* return a signed message digest* throws GeneralSecurityException*/public byte[] sign(byte[] message) throws GeneralSecurityException;
}
UsbKey 数字证书签章
大家一定有 用UsbKey签章的需求因为 软证书是不安全的私钥容易被窃取UsbKey数字证书才是最正规最安全的方案上边说的都是基于软证书的那么Ukey硬证书要怎么签章呢 同样的我们还是利用如下这2个接口。不过这次不用实现digest了因为itext自己包含的digest算法都是可以满足的直接用例子中的代码就可以。 你说我的ukey 证书也是国密SM3withSM2的那怎么办我的回答是国密算法的ukey最好不要用因为还是那句话即便实现了接口签出来pdf后 市面上主流pdf阅读器都不能验签那就失去了签章的意义。除非自己写一个阅读器。。。 可以点开源码看看itext oid中都包含那些算法目前咱们用得到的算法除了国密算法其他的算法基本都可以支持 // 摘要算法ExternalDigest digest new BouncyCastleDigest();;// 签名算法ExternalSignature signature ;
OKUkey怎么调用很简单同样是 实现signature接口把你的ukey的摘要算法和加密算法名称返回 sign函数中 调用你的Ukey的签名算法就行了。 比如你的Ukey是windows平台的Ukey厂家肯定给你有Ukey驱动和 算法dll我们需要做的就是用java写个jni接口添加native方法然后javah生成.h头文件然后在用c或者c调用厂家的dll实现jni接口 然后在 ExternalSignature的sign方法中调用native方法就可以了。 当然自己封装的 签名函数传入的参数就要变一变了例如私钥 就直接传入null
import java.io.File;public class NativeMethods {static {String parentPathD:\\dll\\;String dllNameNativeCode.dll;File dllnew File(parentPathdllName);if (dll.exists()) {
// System.loadLibrary(dllName);//dll必须方法系统环境变量下System.load(parentPathdllName); //可以指定任意位置}}//进行签名传入签名原文返回签名结果public native String signByUKEY(String message);//获取签名公钥证书public native String getSignCer();
}
使用远程服务器签名的方式签章
看了上边内容估计你也会举一反三的 实现 服务器形式的 签名了没错就是实现 签名接口ExternalSignature signature ;在sign方法中访问 服务器签名接口 传送签名原文返回签名结果就可以了。
结语
OK了itext 进行pdf签章这块就说完了希望对大家有所帮助。