In general you cannot expect the signature creation step to return a LTV-enabled signature.
Leonard Rosenthol (Adobe's prime PDF Guru) remarked on the iText mailing list in early 2013 that while it is possible to have the signature container itself already contain all the information required for a LTV enabled signature, this is very uncommon and not always possible.
(There are exceptions, e.g. there was a Swisscom signing service generating signature containers with all the extra information required for a LTV enabled integrated PDF signature.)
Thus, in general you'll have to add all the missing information in a second step.
Such a second step, on the other hand, means that such a second pass may interfere with signing with CertificationLevel = CERTIFIED_NO_CHANGES_ALLOWED
- the current PDF specification requires that even for such a certification level incremental updates are allowed if they contain only signature validation information but I have not yet seen Adobe Reader not complain about in such a case. So you might have to loosen your certification level for LTV enabling.
For iText 5 / Java and iText 7 / Java helper classes that execute this second step can be found in this answer (iText 5) and this answer (iText 7).
I have ported the Java helper class for iText 5 to C#:
using iTextSharp.text;
using iTextSharp.text.error_messages;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.security;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Ocsp;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Ocsp;
using Org.BouncyCastle.X509;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using System.Text;
[...]
class AdobeLtvEnabling
{
/**
* Use this constructor with a {@link PdfStamper} in append mode. Otherwise
* the existing signatures will be damaged.
*/
public AdobeLtvEnabling(PdfStamper pdfStamper)
{
this.pdfStamper = pdfStamper;
}
/**
* Call this method to have LTV information added to the {@link PdfStamper}
* given in the constructor.
*/
public void enable(IOcspClient ocspClient, ICrlClient crlClient)
{
AcroFields fields = pdfStamper.AcroFields;
bool encrypted = pdfStamper.Reader.IsEncrypted();
List<String> names = fields.GetSignatureNames();
foreach (String name in names)
{
PdfPKCS7 pdfPKCS7 = fields.VerifySignature(name);
PdfDictionary signatureDictionary = fields.GetSignatureDictionary(name);
X509Certificate certificate = pdfPKCS7.SigningCertificate;
addLtvForChain(certificate, ocspClient, crlClient, getSignatureHashKey(signatureDictionary, encrypted));
}
outputDss();
}
//
// the actual LTV enabling methods
//
void addLtvForChain(X509Certificate certificate, IOcspClient ocspClient, ICrlClient crlClient, PdfName key)
{
if (seenCertificates.Contains(certificate))
return;
seenCertificates.Add(certificate);
ValidationData validationData = new ValidationData();
while (certificate != null)
{
Console.WriteLine(certificate.SubjectDN);
X509Certificate issuer = getIssuerCertificate(certificate);
validationData.certs.Add(certificate.GetEncoded());
byte[] ocspResponse = ocspClient.GetEncoded(certificate, issuer, null);
if (ocspResponse != null)
{
Console.WriteLine(" with OCSP response");
validationData.ocsps.Add(ocspResponse);
X509Certificate ocspSigner = getOcspSignerCertificate(ocspResponse);
if (ocspSigner != null)
{
Console.WriteLine(" signed by {0}
", ocspSigner.SubjectDN);
}
addLtvForChain(ocspSigner, ocspClient, crlClient, getOcspHashKey(ocspResponse));
}
else
{
ICollection<byte[]> crl = crlClient.GetEncoded(certificate, null);
if (crl != null && crl.Count > 0)
{
Console.WriteLine(" with {0} CRLs
", crl.Count);
foreach (byte[] crlBytes in crl)
{
validationData.crls.Add(crlBytes);
addLtvForChain(null, ocspClient, crlClient, getCrlHashKey(crlBytes));
}
}
}
certificate = issuer;
}
validated[key] = validationData;
}
void outputDss()
{
PdfWriter writer = pdfStamper.Writer;
PdfReader reader = pdfStamper.Reader;
PdfDictionary dss = new PdfDictionary();
PdfDictionary vrim = new PdfDictionary();
PdfArray ocsps = new PdfArray();
PdfArray crls = new PdfArray();
PdfArray certs = new PdfArray();
writer.AddDeveloperExtension(PdfDeveloperExtension.ESIC_1_7_EXTENSIONLEVEL5);
writer.AddDeveloperExtension(new PdfDeveloperExtension(PdfName.ADBE, new PdfName("1.7"), 8));
PdfDictionary catalog = reader.Catalog;
pdfStamper.MarkUsed(catalog);
foreach (PdfName vkey in validated.Keys)
{
PdfArray ocsp = new PdfArray();
PdfArray crl = new PdfArray();
PdfArray cert = new PdfArray();
PdfDictionary vri = new PdfDictionary();
foreach (byte[] b in validated[vkey].crls)
{
PdfStream ps = new PdfStream(b);
ps.FlateCompress();
PdfIndirectReference iref = writer.AddToBody(ps, false).IndirectReference;
crl.Add(iref);
crls.Add(iref);
}
foreach (byte[] b in validated[vkey].ocsps)
{
PdfStream ps = new PdfStream(buildOCSPResponse(b));
ps.FlateCompress();
PdfIndirectReference iref = writer.AddToBody(ps, false).IndirectReference;
ocsp.Add(iref);
ocsps.Add(iref);
}
foreach (byte[] b in validated[vkey].certs)
{
PdfStream ps = new PdfStream(b);
ps.FlateCompress();
PdfIndirectReference iref = writer.AddToBody(ps, false).IndirectReference;
cert.Add(iref);
certs.Add(iref);
}
if (ocsp.Length > 0)
vri.Put(PdfName.OCSP, writer.AddToBody(ocsp, false).IndirectReference);
if (crl.Length > 0)
vri.Put(PdfName.CRL, writer.AddToBody(crl, false).IndirectReference);
if (cert.Length > 0)
vri.Put(PdfName.CERT, writer.AddToBody(cert, false).IndirectReference);
vri.Put(PdfName.TU, new PdfDate());
vrim.Put(vkey, writer.AddToBody(vri, false).IndirectReference);
}
dss.Put(PdfName.VRI, writer.AddToBody(vrim, false).IndirectReference);
if (ocsps.Length > 0)
dss.Put(PdfName.OCSPS, writer.AddToBody(ocsps, false).IndirectReference);
if (crls.Length > 0)
dss.Put(PdfName.CRLS, writer.AddToBody(crls, false).IndirectReference);
if (certs.Length > 0)
dss.Put(PdfName.CERTS, writer.AddToBody(certs, false).IndirectReference);
catalog.Put(PdfName.DSS, writer.AddToBody(dss, false).IndirectReference);
}
//
// VRI signature hash key calculation
//
static PdfName getCrlHashKey(byte[] crlBytes)
{
X509Crl crl = new X509Crl(CertificateList.GetInstance(crlBytes));
byte[] signatureBytes = crl.GetSignature();
DerOctetString octetString = new DerOctetString(signatureBytes);
byte[] octetBytes = octetString.GetEncoded();
byte[] octetHash = hashBytesSha1(octetBytes);
PdfName octetName = new PdfName(Utilities.ConvertToHex(octetHash));
return octetName;
}
static PdfName getOcspHashKey(byte[] basicResponseBytes)
{
BasicOcspResponse basicResponse = BasicOcspResponse.GetInstance(Asn1Sequence.GetInstance(basicResponseBytes));
byte[] signatureBytes = basicResponse.Signature.GetBytes();
DerOctetString octetString = new DerOctetString(signatureBytes);
byte[] octetBytes = octetString.GetEncoded();
byte[] octetHash = hashBytesSha1(octetBytes);
PdfName octetName = new PdfName(Utilities.ConvertToHex(octetHash));
return octetName;
}
static PdfName getSignatureHashKey(PdfDictionary dic, bool encrypted)
{
PdfString contents = dic.GetAsString(PdfName.CONTENTS);
byte[] bc = contents.GetOriginalBytes();
if (PdfName.ETSI_RFC3161.Equals(PdfReader.GetPdfObject(dic.Get(PdfName.SUBFILTER))))
{
using (Asn1InputStream din = new Asn1InputStream(bc))
{
Asn1Object pkcs = din.ReadObject();
bc = pkcs.GetEncoded();
}
}
byte[] bt = hashBytesSha1(bc);
return new PdfName(Utilities.ConvertToHex(bt));
}
static byte[] hashBytesSha1(byte[] b)
{
SHA1 sha = new SHA1CryptoServiceProvider();
return sha.ComputeHash(b);
}
//
// OCSP response helpers
//
static X509Certificate getOcspSignerCertificate(byte[] basicResponseBytes)
{
BasicOcspResponse borRaw = BasicOcspResponse.GetInstance(Asn1Sequence.GetInstance(basicResponseBytes));
BasicOcspResp bor = new BasicOcspResp(borRaw);
foreach (X509Certificate x509Certificate in bor.GetCerts())
{
if (bor.Verify(x509Certificate.GetPublicKey()))
return x509Certificate;
}
return null;
}
static byte[] buildOCSPResponse(byte[] BasicOCSPResponse)
{
DerOctetString doctet = new DerOctetString(BasicOCSPResponse);
Asn1EncodableVector v2 = new Asn1EncodableVector();
v2.Add(OcspObjectIdentifiers.PkixOcspBasic);
v2.Add(doctet);
DerEnumerated den = new DerEnumerated(0);
Asn1EncodableVector v3 = new Asn1EncodableVector();
v3.Add(den);
v3.Add(new DerTaggedObject(true, 0, new DerSequence(v2)));
DerSequence seq = new DerSequence(v3);
return seq.GetEncoded();
}
//
// X509 certificate related helpers
//
static X509Certificate getIssuerCertificate(X509Certificate certificate)
{
String url = getCACURL(certificate);
if (url != null && url.Length > 0)
{
HttpWebRequest con = (HttpWebRequest)WebRequest.Create(url);
HttpWebResponse response = (HttpWebResponse)con.GetResponse();
if (response.StatusCode != HttpStatusCode.OK)
throw new IOException(MessageLocalization.GetComposedMessage("invalid.http.response.1", (int)response.StatusCode));
//Get Response
Stream inp = response.GetResponseStream();
byte[] buf = new byte[1024];
MemoryStream bout = new MemoryStream();
while (true)
{
int n = inp.Read(buf, 0, buf.Length);
if (n <= 0)
break;
bout.Write(buf, 0, n);
}
inp.Close();
var cert2 =