PDF时间戳数字签名
可信时间戳是由时间戳服务中心(TSA:Time Stamp Authority)颁发的具有法律效力的电子凭证, 时间戳与电子数据唯一对应,其中包含电子数据 “指纹”、产生时间、时间戳服务中心信息等。
可信时间戳的法律效力、作用我就不说了 直接贴代码吧。
import com.itextpdf.text.DocumentException; import com.itextpdf.text.Rectangle; import com.itextpdf.text.pdf.*; import java.io.*; import java.security.MessageDigest; import java.security.SignatureException; import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; import java.util.Calendar; import java.util.HashMap; /** * Created by zhangzhenhua on 2016/11/1. */ public class PDFSigner { //tsa private SignerKeystore signerKeystore; private TSAClient tsaClient; private PDFSigner(){} /** * * @param tsa_url tsa服务器地址 * @param tsa_accnt tsa账户号 * @param tsa_passw tsa密码 * @param cert_path 证书路径 * @param cert_passw 证书密码 */ public PDFSigner(String tsa_url,String tsa_accnt,String tsa_passw,String cert_path,String cert_passw) { tsaClient = new TSAClientBouncyCastle(tsa_url, tsa_accnt, tsa_passw); try { signerKeystore = new SignerKeystorePKCS12(new FileInputStream(cert_path), cert_passw); } catch (Exception e) { e.printStackTrace(); } } /** * TSA时间戳签名 * @param infilePath 未签名的文件路径 * @param outfilePath 签名后的文件路径 * @throws Exception */ public void signPDF(String infilePath,String outfilePath) throws Exception { PdfReader reader = new PdfReader(infilePath); FileOutputStream fout = new FileOutputStream(outfilePath); PdfStamper stp = PdfStamper.createSignature(reader, fout, '\0'); PdfSignatureAppearance sap = stp.getSignatureAppearance(); sap.setCrypto(null, this.signerKeystore.getChain(), null, PdfSignatureAppearance.SELF_SIGNED); sap.setVisibleSignature(new Rectangle(100, 100, 300, 200), 1, "Signature"); PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, new PdfName("adbe.pkcs7.detached")); dic.setReason(sap.getReason()); dic.setLocation(sap.getLocation()); dic.setContact(sap.getContact()); dic.setDate(new PdfDate(sap.getSignDate())); sap.setCryptoDictionary(dic); int contentEstimated = 15000; HashMap exc = new HashMap(); exc.put(PdfName.CONTENTS, new Integer(contentEstimated * 2 + 2)); sap.preClose(exc); PdfPKCS7 sgn = new PdfPKCS7(this.signerKeystore.getPrivateKey(), this.signerKeystore.getChain(), null, "SHA1", null, false); InputStream data = sap.getRangeStream(); MessageDigest messageDigest = MessageDigest.getInstance("SHA1"); byte buf[] = new byte[8192]; int n; while ((n = data.read(buf)) > 0) { messageDigest.update(buf, 0, n); } byte hash[] = messageDigest.digest(); Calendar cal = Calendar.getInstance(); byte[] ocsp = null; if ( this.signerKeystore.getChain().length >= 2) { String url = PdfPKCS7.getOCSPURL((X509Certificate) this.signerKeystore.getChain()[0]); if (url != null && url.length() > 0) ocsp = new OcspClientBouncyCastle((X509Certificate) this.signerKeystore.getChain()[0], (X509Certificate) this.signerKeystore.getChain()[1], url).getEncoded(); } byte sh[] = sgn.getAuthenticatedAttributeBytes(hash, cal, ocsp); sgn.update(sh, 0, sh.length); byte[] encodedSig = sgn.getEncodedPKCS7(hash, cal, this.tsaClient, ocsp); if (contentEstimated + 2 < encodedSig.length) throw new Exception("Not enough space"); byte[] paddedSig = new byte[contentEstimated]; System.arraycopy(encodedSig, 0, paddedSig, 0, encodedSig.length); PdfDictionary dic2 = new PdfDictionary(); dic2.put(PdfName.CONTENTS, new PdfString(paddedSig).setHexWriting(true)); sap.close(dic2); } public static void main(String[] args) { //test String TSA_URL = "http://tsa.safelayer.com:8093"; String TSA_ACCNT = ""; String TSA_PASSW = ""; String IN_FILE = "E:\\项目\\paperless\\lipsum.pdf"; String OUT_FILE = "E:\\项目\\paperless\\test_signed.pdf"; String CERT_PATH = "E:\\项目\\paperless\\bfnsh.pfx"; String CERT_PASSW = "123456"; PDFSigner signer = new PDFSigner(TSA_URL,TSA_ACCNT,TSA_PASSW,CERT_PATH,CERT_PASSW); try { signer.signPDF(IN_FILE,OUT_FILE); } catch (Exception e) { e.printStackTrace(); } } }
以上是签名类,再贴一下辅助读取证书的类
import java.security.PrivateKey; import java.security.Provider; import java.security.cert.Certificate; /** * Created by zhangzhenhua on 2016/10/28. */ public interface SignerKeystore { public PrivateKey getPrivateKey() ; public Certificate[] getChain() ; public Provider getProvider(); }
/** * Created by hmt on 2016/10/28. */ import java.io.InputStream; import java.security.KeyStore; import java.security.PrivateKey; import java.security.Provider; import java.security.Security; import java.security.cert.Certificate; /** * SignerKeystore implementation using PKCS#12 file (.pfx etc) */ public class SignerKeystorePKCS12 implements SignerKeystore { private static Provider prov = null; private KeyStore ks; private String alias; private String pwd; private PrivateKey key; private Certificate[] chain; public SignerKeystorePKCS12(InputStream inp, String passw) throws Exception { // This should be done once only for the provider... if (prov == null) { prov = new org.bouncycastle.jce.provider.BouncyCastleProvider(); Security.addProvider(prov); } this.ks = KeyStore.getInstance("pkcs12", prov); this.pwd = passw; this.ks.load(inp, pwd.toCharArray()); this.alias = (String)ks.aliases().nextElement(); this.key = (PrivateKey)ks.getKey(alias, pwd.toCharArray()); this.chain = ks.getCertificateChain(alias); } public PrivateKey getPrivateKey() { return key; } public Certificate[] getChain() { return chain; } public Provider getProvider() { return ks.getProvider(); } }
测试结果