After Adding LTV to digital signature it shows document has changed.
After taking ref from this que : After LTV Certification Signature, PDF shows "Document has been Changed"
I made changes in my code, It works fine with all document but for this document : https://www.sendspace.com/file/3ulwn7 - It shows Invalid signature.
we are also using document signing service from global sign for same.
Below code for adding LTV :
public void AddLtv(string src, string dest, IOcspClient ocsp, ICrlClient crl, ITSAClient tsa)
{
using (PdfReader r = new PdfReader(src))
{
using (FileStream fos =new FileStream(dest,FileMode.CreateNew))
{
PdfStamper stp = new PdfStamper(r, fos, '\0', true);
LtvVerification v = stp.LtvVerification;
AcroFields fields = stp.AcroFields;
List<String> names = fields.GetSignatureNames();
String sigName = names[names.Count - 1];
PdfPKCS7 pkcs7 = fields.VerifySignature(sigName);
if (pkcs7.IsTsp)
{
v.AddVerification(sigName, ocsp, crl,
LtvVerification.CertificateOption.SIGNING_CERTIFICATE,
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);
}
}
stp.Close();
}
}
}
Edit: I think the way i operate pdf in code is causing issue in reading/writing pdf . Somehow i could not get any validator for pdf which can identify issue which @mkl told about cross pdf ref. Yet i am sharing my code as below if there is any problem in how i operate pdf. Help would be appreciated.
This pdf's old signature becomes invalid when I add new one. and if i add LTV then it is invalid for even single signature.
unsigned pdf URL: https://www.sendspace.com/file/n0ckem
Signed pdf without LTV URL : https://www.sendspace.com/file/t1gwp9
Signed pdf with LTV single sign: https://www.sendspace.com/file/ba8leq
Signed pdf with LTV two sign: https://www.sendspace.com/file/6b53z1
Below code for creating empty container and adding signature :
private async Task<string> SignPdf(string ocspResponse, string cert, string unsignedPdf, DocumentShapeModel annotations, string caCertraw, int pageHeight, UserProfileModel user)
{
var trustedSignedpdf = Path.Combine(_env.WebRootPath, "TempPath", annotations.userId, annotations.Id.ToString(), "trustedSignedpdf.pdf");
if (!Directory.Exists(Path.GetDirectoryName(trustedSignedpdf)))
{
Directory.CreateDirectory(trustedSignedpdf);
}
var tempPdf = Path.Combine(_env.WebRootPath, "TempPath", annotations.userId, annotations.Id.ToString());
if (!Directory.Exists(tempPdf))
{
Directory.CreateDirectory(tempPdf);
}
tempPdf = Path.Combine(tempPdf, "tempSignedpdfglobal.pdf");
string finalsignedPdf = trustedSignedpdf;
var ocsp = new OcspClientBouncyCastle();
byte[] oc2 = Convert.FromBase64String(oc1);
OcspResp ocspResp = new OcspResp(oc2);
BasicOcspResp basicResp = (BasicOcspResp)ocspResp.GetResponseObject();
byte[] oc = basicResp.GetEncoded();
bool check = false;
string hexencodedDigest = null;
PdfPKCS7 sgn = null;
byte[] hash = null;
Org.BouncyCastle.X509.X509Certificate[] chain = new Org.BouncyCastle.X509.X509Certificate[2];
var cer = new Org.BouncyCastle.X509.X509CertificateParser()
.ReadCertificate((new X509Certificate2(cert)).GetRawCertData());
chain[0] = cer;
var caCert = new Org.BouncyCastle.X509.X509CertificateParser()
.ReadCertificate((new X509Certificate2(caCertraw)).GetRawCertData());
chain[1] = caCert;
while (!check)
{
PdfReader.unethicalreading = true;
//create empty signature
using (PdfReader reader = new PdfReader(unsignedPdf))
{
using (FileStream os = File.OpenWrite(tempPdf))
{
PdfStamper pdfStamper = PdfStamper.CreateSignature(reader, os, '\0', null, true);
PdfSignatureAppearance signatureAppearance = pdfStamper.SignatureAppearance;
// Sets Signature Appearance
signatureAppearance.Certificate = chain[0];
signatureAppearance.CertificationLevel = PdfSignatureAppearance.NOT_CERTIFIED;
signatureAppearance.Reason = "E Signed by " + user.FirstName + " " + user.LastName + " (" + user.Email + ").";
signatureAppearance.Acro6Layers = false;
signatureAppearance.Layer4Text = PdfSignatureAppearance.questionMark;
float shapeH = annotations.IsResponsive == true ? annotations.h : ((annotations.h * 72 / 150) / (float)Convert.ToDouble(annotations.ratio));
float shapeX = annotations.IsResponsive == true ? annotations.x : ((annotations.x * 72 / 150) / (float)Convert.ToDouble(annotations.ratio));
float shapeY = annotations.IsResponsive == true ? annotations.y : ((annotations.y * 72 / 150) / (float)Convert.ToDouble(annotations.ratio));
float shapeW = annotations.IsResponsive == true ? annotations.w : ((annotations.w * 72 / 150) / (float)Convert.ToDouble(annotations.ratio));
double yaxis = (float)Convert.ToDouble(pageHeight) - (shapeH + shapeY);
// Sets Layer2 text and acro6layers
signatureAppearance.Layer2Text = " "; //Left blank so that it do not overwrite Esignature.
signatureAppearance.SetVisibleSignature(new iTextSharp.text.Rectangle((int)(shapeX), (int)yaxis, (int)(shapeX) + (int)shapeW, (int)yaxis + (int)shapeH), annotations.p, annotations.Id.ToString());
IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
MakeSignature.SignExternalContainer(signatureAppearance, external, 8192);
Stream data = signatureAppearance.GetRangeStream();
string hashAlgorithm = "SHA256";
sgn = new PdfPKCS7(null, chain, hashAlgorithm, false);
hash = DigestAlgorithms.Digest(data, hashAlgorithm);
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, oc, null, CryptoStandard.CADES);
//create sha256 message digest
using (SHA256.Create())
{
sh = SHA256.Create().ComputeHash(sh);
}
//create hex encoded sha256 message digest
hexencodedDigest = new BigInteger(1, sh).ToString(16);
hexencodedDigest = hexencodedDigest.ToUpper();
if (hexencodedDigest.Length == 64)
{
check = true;
}
}
}
}
var identityGetResult = await IdentityGet(_appConfiguration.TrustedSignSettings.Url + "/identity", AccessToken, IdentityJson, hexencodedDigest);
//decode hex
byte[] dsg = FromHex(identityGetResult);
//include signature on PDF
sgn.SetExternalDigest(dsg, null, "RSA");
//create TimeStamp Client
ITSAClient tsc = new DssClient(AccessToken, _env, _appConfiguration.TrustedSignSettings.Url);
//byte[] ocspResponse = ocsp.GetEncoded(chain[0],chain[chain.Length -1], CertificateUtil.GetCRLURL(chain[0]));
//Collection<byte[]> crlBytes = CertificateUtil.fetchCrlBytes(x509certificate, chain);
byte[] encodedpkcs7 = sgn.GetEncodedPKCS7(hash, tsc, oc, null, CryptoStandard.CADES);
//adds PKCS7 format Signature on empty signature container
CreateSignature(tempPdf, finalsignedPdf, annotations.Id.ToString(), encodedpkcs7);
var finaltrustedSignedpdf = Path.Combine(_env.WebRootPath, "TempPath", annotations.userId, annotations.Id.ToString());
if (!Directory.Exists(finaltrustedSignedpdf))
{
Directory.CreateDirectory(finaltrustedSignedpdf);
}
finaltrustedSignedpdf = Path.Combine(finaltrustedSignedpdf, "FinaltrustedSignedpdf.pdf");
//adds LTV to signed document
AddLtv(finalsignedPdf, finaltrustedSignedpdf, ocsp, new CrlClientOnline(), tsc);
return finaltrustedSignedpdf;
}
For creating signature
public void CreateSignature(string src, string dest, string fieldname, byte[] sig)
{
using (PdfReader reader = new PdfReader(src))
{
using (FileStream os = File.OpenWrite(dest))
{
IExternalSignatureContainer external = new MyExternalSignatureContainer(sig);
MakeSignature.SignDeferred(reader, fieldname, os, external);
}
}
}
There is an error in the cross reference table of the original PDF. Adobe signature validation is known to be sensitive to such errors (see this answer and this answer), under certain circumstances it shows signatures of such files as invalid.
You should ask the source of that document to provide a version without that error
The details
The cross reference table of the first, unsigned revision of the document looks like this:
As you see it consists of two subsections, the first one for 55 objects starting at 0, the second one of 18 objects starting at 54.
This is invalid for two reasons:
First of all (as already has been explained in the two answers referenced above) the cross reference table of the initial PDF revision must consist of a single section only!
(ISO 32000-1 and ISO 32000-2, in both cases section 7.5.4 "Cross-Reference Table")
Furthermore, that cross reference table has two entries for the same object, both the last entry of the first subsection and the first entry of the second subsection relate to object 54. This also is forbidden:
(ibidem)
Depending on details of the respective code this may or may not result in arbitrary problems when processing the PDF with some PDF processor, e.g. Adobe Acrobat Reader.
Your edit
In your edit you shared a number of files. In particular you shared
VeriFinger_SDK_Brochure_2017-12-27.pdf
VeriFinger_SDK_Brochure_signed_witoutltv.Pdf
FinaltrustedSignedpdf.pdf
FinaltrustedSignedpdf.pdf (same name but differing from the previous)
According to your code you apply all changes in append mode. Thus, the three latter files each have to consist of the first file VeriFinger_SDK_Brochure_2017-12-27.pdf plus some appended data. But that is not the case, the three latter files actually all are shorter than the first one. Thus, I have to assume that that first file is first processed somehow and then signed.
Now looking at the cross reference table of the "original file" VeriFinger_SDK_Brochure_2017-12-27.pdf (simply open it in a text viewer and scroll to its end) we see it is in one piece, just a single subsection. It contains a number of entries marked as free but no gaps.
Looking at the cross reference tables of the first revisions of the latter three files, though, we see that each of them is split into multiple subsections. Apparently runs of entries marked free have been cut out of the table resulting in a table with many subsections. Probably this has been designed as an optimization attempt but the result is a damaged PDF.
Thus, whatever PDF processor it is which you apply to your files before signing, exactly that processor damages the PDF.
The PDF processor that damages the PDF
Comparing the document information of your original file and the initial revisions in the other three files, a PDF processor handling the file before it is signed appears to be Aspose.PDF for .NET 19.1 because the Producer value is changed to that.
And indeed, this appears to be a known Aspose issue, see for example the PDF/A-1 conversion creates invalid XRef table thread on the Aspose free support forum, started in August 2016.
It has been filed as PDFNET-41272 and marked as fixed in Aspose.Pdf for .NET 17.2.0 in February 2017, but as reported on that very forum thread in the same month, it was not really fixed at all.
Apparently Aspose has not yet fixed this bug and still is working on it.