2

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);
        }
    }
}
mkl
  • 90,588
  • 15
  • 125
  • 265
  • Although it seems like your issue has been sorted out. However, if the issue is caused by Aspose.PDF while signing the PDF document, could you please make sure that you are using the latest version of the API i.e. 21.8, and if the issue still persists, please share your sample file along with the sample code snippet in our official Aspose.PDF support forum (https://forum.aspose.com/c/pdf/10) which is the right place to report and track such issues. This is Asad Ali and I work as Developer Evangelist at Aspose. – Asad Ali Aug 16 '21 at 18:25

1 Answers1

3

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:

xref
0 55
0000000000 65535 f
0000000018 00000 n
0000000164 00000 n
0000000216 00000 n
0000000554 00000 n
0000003363 00000 n
0000003529 00000 n
0000003764 00000 n
0000003815 00000 n
0000003866 00000 n
0000004038 00000 n
0000004279 00000 n
0000004439 00000 n
0000004662 00000 n
0000004792 00000 n
0000004818 00000 n
0000004991 00000 n
0000005061 00000 n
0000005297 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000005466 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000006188 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000006236 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
54 18
0000006284 00000 n
0000006350 00000 n
0000006648 00000 n
0000171077 00000 n
0000171435 00000 n
0000171726 00000 n
0000171973 00000 n
0000323100 00000 n
0000323123 00000 n
0000324290 00000 n
0000324333 00000 n
0000324715 00000 n
0000326153 00000 n
0000328056 00000 n
0000328093 00000 n
0000328132 00000 n
0000328214 00000 n
0000328377 00000 n

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!

    For a file that has never been incrementally updated, the cross-reference section shall contain only one subsection, whose object numbering begins at 0.

    (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:

    A given object number shall not have an entry in more than one subsection within a single section.

    (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

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.

mkl
  • 90,588
  • 15
  • 125
  • 265
  • Thanks i understood the problem. Is there any way using C# code to detect pdf is valid pdf to process further for signing ? – Urmi_VV_Developer May 02 '19 at 12:50
  • Essentially you need a pdf validator, a good one. I haven't checked them for this use case. – mkl May 02 '19 at 15:54
  • Somehow i could not find/get a proper pdf validator for the issue of cross ref table you told. In some validators it shows true only when i check pdf while doing that i obsevered same behaviour in other pdf too which failed in create signature only, for which i shared code here in updated question, if you can help. Thanks – Urmi_VV_Developer May 07 '19 at 11:00
  • Related: iText7 in version 7.0.4 had a similar bug as Aspose but that meanwhile has been fixed, see [this answer](https://stackoverflow.com/a/45163411/1729265). It appears to be a common error... ;) – mkl May 07 '19 at 15:24
  • Yes, It was worked when i do not use aspose as processor for my pdf and solved problem. what I am really looking for is a way to detect before processing any PDF if it has single section of Xref or multiple? That is needed, if you can help in same. – Urmi_VV_Developer May 08 '19 at 07:19
  • I'm afraid I don't know any validator detecting this detail. Probably there isn't any yet, otherwise that Aspose bug would have caused more of a stir and would have been resolved a long time ago. I assume it should be possible to derive a validator for this from the cross reference table processing code in iText. That's a bit of work though and definitely too broad for a stack overflow question. – mkl May 08 '19 at 08:22
  • 1
    @urmi One possible option for you, though: If you can count on the incoming PDFs not being signed before, you can try and load the source PDF in a `PdfReader` and write it to another file without content changes using a `PdfStamper` with append mode switched off. This makes iText save the whole copy in its own way. I would hope that that would result in a single-subsection xref table. – mkl May 08 '19 at 08:26
  • This worked like charm, But in recently i found that in some adobe reader even if the LTV is added, It shows LTV not enabled moreover suppose there are 3 certificates in signed doc and showing valid in my adobe but in other person's adobe reader it is only one or two certs middle certificate is missing or only last certificate in present in chain. why Adobe is behaving differently i could not understand. Can You please help. @mkl – Urmi_VV_Developer Nov 04 '19 at 06:20
  • Please make that a question in its own right, describe exactly how you process the files, and share example files before and after processing. – mkl Nov 04 '19 at 09:24
  • Thanks for looking @mkl , please check newly posted question here https://stackoverflow.com/questions/58691511/shows-ltv-not-enabled-for-ltv-enabled-documents-certificate-missing-from-hierar – Urmi_VV_Developer Nov 04 '19 at 10:29