How to enable LTV for a timestamp signature?
Acrobat tells me my signature is LTV enabled, but the timestamp signature is not. How can I make both LTV enabled?
I'm using iText to sign PDF documents. I need these documents to be timestamped and LTV-enabled. I followed the instructions and used the addLtv()
method. I get a PDF with 2 signatures, which is normal: the first is my own signature, the second is the document-level timestamp.
However, Acrobat tells me my signature is LTV enabled, but the timestamp signature is not:
This is because the revocation info of the timestamp certificate is not embedded in the document :
From my understanding, the addLtv()
method should get all revocation information needed and embed it in the document. Is that correct, or do I have to "manually" get and embed this information?
Posted on StackOverflow on Jan 11, 2015 by Silas
Answer provided by mkl:
This is the sample code this question is about:
void addLTV(String src, String dest, IOcspClient ocsp, ICrlClient crl, ITSAClient itsaClient)
throws IOException, GeneralSecurityException {
PdfReader reader = new PdfReader(src);
PdfWriter writer = new PdfWriter(dest);
PdfDocument pdfDoc = new PdfDocument(reader, writer, new StampingProperties().useAppendMode());
LtvVerification v = new LtvVerification(pdfDoc);
SignatureUtil signatureUtil = new SignatureUtil(pdfDoc);
List<String> names = signatureUtil.getSignatureNames();
String sigName = names.get(names.size() - 1);
PdfPKCS7 pkcs7 = signatureUtil.readSignatureData(sigName);
if (pkcs7.isTsp()) {
v.addVerification(sigName, ocsp, crl, LtvVerification.CertificateOption.WHOLE_CHAIN,
LtvVerification.Level.OCSP_CRL, LtvVerification.CertificateInclusion.NO);
} else {
for (String name : names) {
v.addVerification(name, ocsp, crl, LtvVerification.CertificateOption.WHOLE_CHAIN,
LtvVerification.Level.OCSP_CRL, LtvVerification.CertificateInclusion.NO);
}
}
v.merge();
// document timestamp
FileOutputStream fileOutputStream = new FileOutputStream(dest);
PdfSigner ps = new PdfSigner(reader, fileOutputStream, new StampingProperties().useAppendMode());
ps.timestamp(itsaClient, null);
}
void addLTV(String src, String dest, IOcspClient ocsp, ICrlClient crl, ITSAClient itsaClient)
{
PdfReader reader = new PdfReader(src);
PdfWriter writer = new PdfWriter(dest);
PdfDocument pdfDoc = new PdfDocument(reader, writer, new StampingProperties().UseAppendMode());
LtvVerification v = new LtvVerification(pdfDoc);
SignatureUtil signatureUtil = new SignatureUtil(pdfDoc);
IList names = signatureUtil.GetSignatureNames();
String sigName = names[names.Count - 1];
PdfPKCS7 pkcs7 = signatureUtil.ReadSignatureData(sigName);
if (pkcs7.IsTsp()){
v.AddVerification(sigName, ocsp, crl, LtvVerification.CertificateOption.WHOLE_CHAIN,
LtvVerification.Level.OCSP_CRL, LtvVerification.CertificateInclusion.NO);
}else{
foreach (var name in names){
v.AddVerification(name, ocsp, crl, LtvVerification.CertificateOption.WHOLE_CHAIN,
LtvVerification.Level.OCSP_CRL, LtvVerification.CertificateInclusion.NO);
}
}
v.Merge();
FileStream fileOutputStream = new FileStream(dest, FileMode.Create);
PdfSigner ps = new PdfSigner(reader, fileOutputStream, new StampingProperties().useAppendMode());
ps.Timestamp(itsaClient, null);
}
This code identifies the most recently filled signature field of the PDF and checks whether it is a document time stamp or an usual signature.
If it is a document time stamp, the code adds validation information only for this document timestamp. Otherwise the code adds validation information for all signatures. (he assumed work flow behind this is that the document is signed (for certification and/or approval) a number of times first, and then the document enters LTV cycles adding validation information and document time stamps but no usual signatures anymore. Your work flow may vary and, therefore, your program logic, too.
Only after all this is done, a new document time stamp is added.
For this finally added time stamp no validation information are explicitly added to the PDF and this is why Adobe Reader/Acrobat usually does not consider this document time stamp LTV enabled. (if document time stamps from the same TSA have been applied in short succession, validation information included for a prior time stamp may be applicable.)
If you need validation information for this final document time stamp too, simply apply this method (the same as the method above, merely not adding a document time stamp) to the file with the document time stamp:
void addLTVNoTs(String src, String dest, IOcspClient ocsp, ICrlClient crl)
throws IOException, GeneralSecurityException {
PdfReader reader = new PdfReader(src);
PdfWriter writer = new PdfWriter(dest);
PdfDocument pdfDoc = new PdfDocument(reader, writer, new StampingProperties().useAppendMode());
LtvVerification v = new LtvVerification(pdfDoc);
SignatureUtil signatureUtil = new SignatureUtil(pdfDoc);
List<String> names = signatureUtil.getSignatureNames();
String sigName = names.get(names.size() - 1);
PdfPKCS7 pkcs7 = signatureUtil.readSignatureData(sigName);
if (pkcs7.isTsp()) {
v.addVerification(sigName, ocsp, crl, LtvVerification.CertificateOption.WHOLE_CHAIN,
LtvVerification.Level.OCSP_CRL, LtvVerification.CertificateInclusion.NO);
} else {
for (String name : names) {
v.addVerification(name, ocsp, crl, LtvVerification.CertificateOption.WHOLE_CHAIN,
LtvVerification.Level.OCSP_CRL, LtvVerification.CertificateInclusion.YES);
}
}
v.merge();
pdfDoc.close();
}
void addLTVNoTS(String src, String dest, IOcspClient ocsp, ICrlClient crl, ITSAClient itsaClient)
{
PdfReader reader = new PdfReader(src);
PdfWriter writer = new PdfWriter(dest);
PdfDocument pdfDoc = new PdfDocument(reader, writer, new StampingProperties().UseAppendMode());
LtvVerification v = new LtvVerification(pdfDoc);
SignatureUtil signatureUtil = new SignatureUtil(pdfDoc);
IList names = signatureUtil.GetSignatureNames();
String sigName = names[names.Count - 1];
PdfPKCS7 pkcs7 = signatureUtil.ReadSignatureData(sigName);
if (pkcs7.IsTsp()){
v.AddVerification(sigName, ocsp, crl, LtvVerification.CertificateOption.WHOLE_CHAIN,
LtvVerification.Level.OCSP_CRL, LtvVerification.CertificateInclusion.NO);
} else {
foreach (var name in names) {
v.AddVerification(name, ocsp, crl, LtvVerification.CertificateOption.WHOLE_CHAIN,
LtvVerification.Level.OCSP_CRL, LtvVerification.CertificateInclusion.NO);
}
}
v.Merge();
pdfDoc.Close();
}
Background
The reason why the iText addLtv()
example does not (necessarily) create LTV-enabled PDFs is that it is nearer to the best practices for LTV as proposed by ETSI in the PAdES specification than to Adobe's best practices for LTV.
According to ETSI TS 102 778-4 V1.1.2 (2009-12) the structure of a PDF document to which LTV is applied is illustrated in figure 2.
The life-time of the protection can be further extended beyond the life-of the last document Time-stamp applied by adding further DSS information to validate the previous last document Time-stamp along with a new document Time-stamp. This is illustrated in figure 3.
On the other hand, according to Adobe (as written by their PDF evangelist Leonard Rosenthol on the iText mailing list in January 2013),
LTV enabled means that all information necessary to validate the file (minus root certs) is contained within. So this statement of yours would be true.
the PDF is signed correctly and contains all necessary certificates, a valid CRL or OSCP response for every certificate
But since the only way for that statement to be true is for the presence of DSS, you must have DSS for LTV-enabled to appear. No timestamp (regular or document level) is required.
Due to this divergence PDF documents with LTV according to ETSI usually are presented by Adobe software to have one not LTV-enabled document time stamp.
See also:
Click How to enable LTV for a timestamp signature? if you want to see how to answer this question in iText 5.