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();

    }

}

测试结果

 

posted @ 2019-02-15 13:19  卧龙传奇  阅读(3384)  评论(1编辑  收藏  举报