动态创建pdf并使用itext pdf进行签名

时间:2015-01-26 11:34:54

标签: pdf itext digital-signature itextpdf

我正在动态创建PDF文档,添加签名字段,然后尝试签名。签名工作正常,但我得到一个例外:

  

“由%认证,无效签名和签名包含无效数据”

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.util.Calendar;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfSignatureAppearance;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.text.pdf.security.BouncyCastleDigest;
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;

//Actual class 
public class SignPdf {

    // Signs the pdf
    public void signPdf(String src, String dest, boolean certified, boolean graphic) throws GeneralSecurityException, IOException, DocumentException{
        String path = "src/certs.pfx";
        String keystore_password = "pwd";
        String key_password = "pwd";
        KeyStore ks = KeyStore.getInstance("PKCS12", new org.bouncycastle.jce.provider.BouncyCastleProvider());
        InputStream is = this.getClass().getResourceAsStream("/certs.pfx");
        ks.load(is, keystore_password.toCharArray());

        String alias = ks.aliases().nextElement();
        PrivateKey pk = (PrivateKey) ks.getKey(alias, key_password.toCharArray());
        Certificate[] chain = ks.getCertificateChain(alias);
        byte[] pdfByteArray = null;
        // creating dynamic document using itext.
        Document document = new Document();
        OutputStream baosPDF = new ByteArrayOutputStream();
        PdfWriter.getInstance(document, baosPDF);
        document.open();
        document.add(new Paragraph("Hello World!"));
        document.close();
        byte[] bytearrayb = ((ByteArrayOutputStream) baosPDF).toByteArray();
        PdfReader reader = new PdfReader(bytearrayb);
        PdfStamper stamper = PdfStamper.createSignature(reader, baosPDF, '\0', null, true);

        PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
        appearance.setReason("Security");
        appearance.setLocation("Footer");
        appearance.setVisibleSignature(new Rectangle(100, 100, 200, 200), 1, "DVA");
        if (certified) appearance.setCertificationLevel(PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED);
        if (graphic) {
            appearance.setSignatureGraphic(Image.getInstance(RESOURCE));
            appearance.setRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC);
        }
        appearance.setSignDate(Calendar.getInstance());
        appearance.setSignatureCreator("test");
        ExternalSignature es = new PrivateKeySignature(pk, "SHA-256", "BC");
        ExternalDigest digest = new BouncyCastleDigest();
        MakeSignature.signDetached(appearance, digest, es, chain, null, null, null, 0, CryptoStandard.CMS);
        baosPDF.flush();
        baosPDF.close();

        BufferedOutputStream fs = new BufferedOutputStream(new FileOutputStream(new File("myFile121.pdf")));
        fs.write(((ByteArrayOutputStream) baosPDF).toByteArray());
        fs.flush();
        fs.close();
    }

    public static void main(String[] args) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        SignPdf signatures = new SignPdf();
        try {
            signatures.signPdf(ORIGINAL, "", true, false);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

1 个答案:

答案 0 :(得分:1)

您可以重新使用ByteArrayOutputStream而不清除它:

OutputStream baosPDF = new ByteArrayOutputStream();
PdfWriter.getInstance(document, baosPDF);
document.open();
document.add(new Paragraph("Hello World!"));
document.close();
byte[] bytearrayb = ((ByteArrayOutputStream) baosPDF).toByteArray();
PdfReader reader = new PdfReader(bytearrayb);
PdfStamper stamper = PdfStamper.createSignature(reader, baosPDF, '\0', null, true);

根据ByteArrayOutputStream来源:

/**
 * Resets the <code>count</code> field of this byte array output 
 * stream to zero, so that all currently accumulated output in the 
 * output stream is discarded. The output stream can be used again, 
 * reusing the already allocated buffer space. 
 *
 * @see     java.io.ByteArrayInputStream#count
 */
public synchronized void reset()

因此,在重新使用前重置:

byte[] bytearrayb = ((ByteArrayOutputStream) baosPDF).toByteArray();
baosPDF.reset();
PdfReader reader = new PdfReader(bytearrayb);
PdfStamper stamper = PdfStamper.createSignature(reader, baosPDF, '\0', null, true);