2

I want to add time stamp to file from third party time server. When pdf is open in acrobat or other pdf viewer I want to see information about time stamp in signatures card or any other. Additionally I want to visualize time stamp graphically as image or text in pdf with time stamp.

I get token from time server:

import org.bouncycastle.tsp.TimeStampResponse;
import org.bouncycastle.tsp.TimeStampToken;

...

public TimeStampToken getTimeStampToken(){

  ...

  return response.getTimeStampToken();
}

Now how to add time stamp to pdf using pdf box?

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;

...

public static void addTimeStamp(final File pdfFile, final File signedPdfFile, TimeStampToken token) {

    try (
        FileInputStream fis1 = new FileInputStream(pdfFile);
        FileOutputStream fos = new FileOutputStream(signedPdfFile);
        FileInputStream fis = new FileInputStream(signedPdfFile);
        PDDocument doc = PDDocument.load(pdfFile)) {
        int readCount;
        final byte[] buffer = new byte[8 * 1024];
        while ((readCount = fis1.read(buffer)) != -1) {
            fos.write(buffer, 0, readCount);
        }

        final PDSignature signature = new PDSignature();
        signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
        signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
        signature.setName("NAME");
        signature.setLocation("LOCATION");
        signature.setReason("REASON");
        signature.setSignDate(Calendar.getInstance());

        doc.addSignature(signature);
        doc.saveIncremental(fos);
    } catch (final Exception e) {
        e.printStackTrace();
    }
}
Adam Michalski
  • 1,722
  • 1
  • 17
  • 38

2 Answers2

1

As you just mention a time stamp, not a signature with a signature time stamp, I assume you mean a document time stamp as per PAdES LTV or PDF-2.

The PDFBox signing examples have been implemented with regular digital signatures in mind, not naked digital time stamps. But you can adapt them fairly easily. CreateVisibleSignature and other examples create the signature bytes to embed in the SignatureInterface method sign implemented in the parent class CreateSignatureBase:

/**
 * SignatureInterface implementation.
 *
 * This method will be called from inside of the pdfbox and create the PKCS #7 signature.
 * The given InputStream contains the bytes that are given by the byte range.
 *
 * This method is for internal use only.
 *
 * Use your favorite cryptographic library to implement PKCS #7 signature creation.
 */
@Override
public byte[] sign(InputStream content) throws IOException
{
    //TODO this method should be private
    try
    {
        List<Certificate> certList = new ArrayList<Certificate>();
        certList.add(certificate);
        Store certs = new JcaCertStore(certList);
        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
        org.bouncycastle.asn1.x509.Certificate cert = org.bouncycastle.asn1.x509.Certificate.getInstance(ASN1Primitive.fromByteArray(certificate.getEncoded()));
        ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA256WithRSA").build(privateKey);
        gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()).build(sha1Signer, new X509CertificateHolder(cert)));
        gen.addCertificates(certs);
        CMSProcessableInputStream msg = new CMSProcessableInputStream(content);
        CMSSignedData signedData = gen.generate(msg, false);
        if (tsaClient != null)
        {
            signedData = signTimeStamps(signedData);
        }
        return signedData.getEncoded();
    }
    catch (GeneralSecurityException e)
    {
        throw new IOException(e);
    }
    catch (CMSException e)
    {
        throw new IOException(e);
    }
    catch (TSPException e)
    {
        throw new IOException(e);
    }
    catch (OperatorCreationException e)
    {
        throw new IOException(e);
    }
}

(CreateSignatureBase)

(BTW, I strongly disagree with the TODO-comment.)

Furthermore, CreateVisibleSignature sets the subfilter of the signature in signPDF:

/**
 * Sign pdf file and create new file that ends with "_signed.pdf".
 *
 * @param inputFile The source pdf document file.
 * @param signedFile The file to be signed.
 * @param tsaClient optional TSA client
 * @param signatureFieldName optional name of an existing (unsigned) signature field
 * @throws IOException
 */
public void signPDF(File inputFile, File signedFile, TSAClient tsaClient, String signatureFieldName) throws IOException
{
    [...]

    PDSignature signature;

    // sign a PDF with an existing empty signature, as created by the CreateEmptySignatureForm example. 
    signature = findExistingSignature(doc, signatureFieldName);

    if (signature == null)
    {
        // create signature dictionary
        signature = new PDSignature();
    }

    // default filter
    signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);

    // subfilter for basic and PAdES Part 2 signatures
    signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);

    [...]
}

To create a PAdES document time stamp, you can simply create a copy of the CreateVisibleSignature example, override the SignatureInterface method sign with an implementation that returns a time stamp token for the data in the given InputStream, replace the line

    signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);

in signPDF with

    signature.setSubFilter(new COSName("ETSI.RFC3161"));

and add a line

    signature.setType(new COSName("DocTimeStamp"));

to set the type to time stamp.

mkl
  • 90,588
  • 15
  • 125
  • 265
0

I wanted to add time stamp to pdf without keystore certs and keys and it looks like this:

public static void signWithTimeStampToken() throws InvalidPasswordException, NoSuchAlgorithmException, IOException, TSPException {
    final File inFile = new File("test.pdf");
    final File outFile = new File("test_signed.pdf");
    final MessageDigest digest = MessageDigest.getInstance("SHA-1");
    final TSAClient tsaClient = new TSAClient(new URL("your service"), null, null, digest);
    PdfTimeStampUtils.signPdf(inFile, outFile, tsaClient);
}

private static void signPdf(final File inFile, final File outFile, final TSAClient tsaClient) throws InvalidPasswordException, IOException, NoSuchAlgorithmException,
        TSPException {
    final PDSignature signature = new PDSignature();
    signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
    signature.setSubFilter(COSName.getPDFName("ETSI.RFC3161"));
    signature.setSignDate(Calendar.getInstance());
    final PDDocument pdf = PDDocument.load(inFile);

    final TimestampSignatureImpl sig = new TimestampSignatureImpl(tsaClient);
    pdf.addSignature(signature, sig);
    pdf.saveIncremental(new FileOutputStream(outFile));
    pdf.close();
}

I am using tsaclient from pdfbox examples TSAClient

When I open signed pdf in acrobat I can see in signatures:

enter image description here

Adam Michalski
  • 1,722
  • 1
  • 17
  • 38
  • In your question you said you *want to visualize time stamp graphically*; usually by this one means an in-content visualization, not the entry on the signature panel. Did I misunderstand you or have you simply dropped that requirement? – mkl Jan 19 '17 at 15:38
  • What I meant is to have ability to add something (custom image or text) to distinguish signed and unsigned without viewing signature like in attached image. But it is only additional ( I am curious ) and my answer does not contain it at all. I placed it because i figured it out how to add time stamp. – Adam Michalski Jan 20 '17 at 10:34