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.