可信时间戳是由时间戳服务中心(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();
}
}
测试结果