Document has been altered or corrupted since it was signed with deffered signing

Hi

I'm working on adapting the sample code from this post:

https://www.syncfusion.com/feedback/20551/support-for-deferred-signing-in-pdf-document

I have a pdf I want to sign with the following service:

https://docs.digidentity.com/#912f1f8e-8e93-4e5c-ba14-1b0ae7175fca

My code is succesfully running, but I'm getting an error when validating the signature in Adobe Reader:

"Document has been altered or corrupted since it was signed"

I'm doing the following things:

  1. Send my initial PDF for signing (just to get the certificate). I'm using the following code to compute the hash of the pdf file:
  2. internal static string ComputeHash(Stream data)
    {
    using (SHA256 SHA256 = SHA256.Create())
    {
    var hash = SHA256.ComputeHash(data);
    return HashToString(hash);
    }
    }


    internal static string HashToString(byte[] message)
    {
    return BitConverter.ToString(message).Replace("-", "").ToLowerInvariant();
    }
  3. Call CreateEmptySignedPDF with the certificate from the API call result
  4. Sign the EmptySignature.pdf file with the API. The resulting base64 string is converted to a byte[] and returned by ExternalSigner.Sign()
Am I doing something wrong in the order of things?



27 Replies 1 reply marked as answer

IJ Irfana Jaffer Sadhik Syncfusion Team June 26, 2023 11:07 AM UTC

We have reviewed the code snippet you provided. We recommend that you use the following overload instead, this will prevent the hash data to get corrupted. Let us know if this resolves the issue:

//Deferred signing without PKCS7 encoding.

PdfSignature.ReplaceEmptySignature(inputFileStream, pdfPassword, outputFileStream, signatureName, externalSigner, publicCertificates, false);


follow the below links for more information,

https://help.syncfusion.com/file-formats/pdf/working-with-digitalsignature?cs-save-lang=1&cs-lang=csharp#deferred-signing-without-pkcs7-encoding 

Sample: https://www.syncfusion.com/downloads/support/directtrac/general/ze/Sample-2012454466

If still you are facing an issue, we request you to share the public certificate and reproducible sample with us. So that we can assist you further in this.




JE Jeroen replied to Irfana Jaffer Sadhik June 26, 2023 03:17 PM UTC

Hi


When I pass "isEncodeSignature" = false, I get "There are errors in the formatting or information contained in this signature".

The certificate also does not seem to be present anymore.

I tried attaching a zip file with my VS solution, but I can't seem to send it.

So here's a link to one-drive:

https://softadvice2-my.sharepoint.com/:u:/g/personal/jeroen_softadvice_eu/EWfGPif5H9ZBhn6SwHM1-7AB1YXpEyoVStm8jGkn6fkRvw?e=Uxin7k



IJ Irfana Jaffer Sadhik Syncfusion Team June 27, 2023 10:22 AM UTC

Currently, we are analyzing the reported behavior with the provided details on our end and we will provide further details on June 30th, 2023



IJ Irfana Jaffer Sadhik Syncfusion Team June 30, 2023 11:59 AM UTC

Thank you for your patience.


After investigating the sample you provided, we found only hash details for the signature. As a result, we were unable to determine the actual cause of the problem you are currently facing. We kindly request you to follow the steps below and share the result details with us:


Step 1: Create an empty signature using your public certificate. You can refer to the example code here: [link to the example](https://github.com/SyncfusionExamples/PDF-Examples/blob/master/Digital%20Signature/Deferred-signing-in-PDF-document/.NET/Deferred-signing-in-PDF-document/Program.cs#L21).


Step 2: When creating the empty signature file, please follow the steps outlined in the sign block. You can find the instructions here: [link to the code](https://github.com/SyncfusionExamples/PDF-Examples/blob/master/Digital%20Signature/Deferred-signing-in-PDF-document/.NET/Deferred-signing-in-PDF-document/Program.cs#L94).


The "message" parameter represents the bytes of a document sequence, but it should be a calculated hash or digest value. Therefore, before externally signing the document, you need to generate a hash or digest value from the message bytes, as indicated below:

public byte[] Sign(byte[] message, out byte[] timeStampResponse)

{

    byte[] digest;

    using (SHA256Managed sha = new SHA256Managed())

    {

        digest = sha.ComputeHash(message);

    }

    SignDocumentHash(digest); // Convert digest bytes to base64/Hex format if requested to send to sign the digest.

    // Set a null value to create an empty signed document.

    byte[] signedBytes = null;

    timeStampResponse = null;

    return signedBytes;

}

 


Step 3: Please use the following overload for DeferredSign:


If your signed digest message already contains encoded PKCS7 information, use the following overload to replace your signed signature bytes:

// Deferred signing without PKCS7 encoding.

PdfSignature.ReplaceEmptySignature(inputFileStream, pdfPassword, outputFileStream, signatureName, externalSigner, publicCertificates, false);


Otherwise, if you need to replace your signed signature bytes, use the following overload:

PdfSignature.ReplaceEmptySignature(inputFileStream, pdfPassword, outputFileStream, signatureName, externalSigner, publicCertificates);


Step 4: Replace your signed signature bytes as shown in the code here: [link to the code](https://github.com/SyncfusionExamples/PDF-Examples/blob/master/Digital%20Signature/Deferred-signing-in-PDF-document/.NET/Deferred-signing-in-PDF-document/Program.cs#L137).

public byte[] Sign(byte[] message, out byte[] timeStampResponse)

{

    // Set the signed hash message to replace an empty signature.

    byte[] signedBytes = _signedHash;

    timeStampResponse = null;

    return signedBytes;

}

 


Please try the above-mentioned steps on your side. If the problem persists, we kindly request you to share your signing block information or complete code details with us for further investigation.


Marked as answer

JE Jeroen July 4, 2023 09:58 AM UTC

Hi

I've gotten it to work!

So I need to sign the message bytes from the original pdf (passsed into the Sign method of the "SignEmpty" class), and use it to replace an empty signature with a second IPdfExternalSigner?

If I write it down like that it makes sense, but I didn't really see it until now.

Is there a reason why the signed hash cannot be used immediately? Why is creating the empty signature necessary?




IJ Irfana Jaffer Sadhik Syncfusion Team July 5, 2023 12:32 PM UTC

We're delighted to hear that the problem has been resolved. The deferred signing works as a client-server interaction. Typically, PDF documents are created/manipulated on the server. If the end-user needs to sign a PDF document, they have to retrieve the entire document for signing and then send back the signed PDF document. With this feature, the customer can generate a hash from a PDF document and send it to the end-user for signing. Once the end-user signs it, they will return the signed hash to the server to apply it to the PDF document.


A diagram of a server

Description automatically generated


Is there a reason why the signed hash cannot be used immediately? Why is creating the empty signature necessary?

In real-time, the signed hash may be encoded with PKCS7 information. Therefore, we recommend creating an empty signature and utilizing our ReplaceEmptySignature functionality to accomplish this.



JE Jeroen July 5, 2023 01:19 PM UTC

Thank you very much.


The API I'm using also supports adding a timestamp to the signature:

https://docs.digidentity.com/#d93588af-df25-4bf0-ac4f-f8a265ae5a4b


I'm still in talks with them as I'm not sure what hash it is they want me to send, but I'm assuming it's the same digest message I sent for signing.

So in our case it would be the message digest received in the Sign method for the SignEmpty Class.

I save the same hash from that message and base64 encode the result.

The response is a base64 encoded string so I just decode that and set it to timeStampResponse in the final signing.

However I'm getting "the timestamp could not be verified".

Is my thinking correct? Do I also need to set the timestamp on the empty signature or something?




IJ Irfana Jaffer Sadhik Syncfusion Team July 6, 2023 06:26 AM UTC

Thank you for the update.


Certainly, we can add a timestamp to the signature. To fulfill your requirements, please follow the steps provided below:


Please refer to the sign block (ExternalSigner) available at the following GitHub link: [ExternalSigner GitHub Link](https://github.com/SyncfusionExamples/PDF-Examples/blob/master/Digital%20Signature/Deferred-signing-in-PDF-document/.NET/Deferred-signing-in-PDF-document/Program.cs#L137).


Within the code snippet, locate the "Sign" method responsible for handling the signing process. Here is the relevant code:


public byte[] Sign(byte[] message, out byte[] timeStampResponse)

{

    // Set the signed hash message to replace an empty signature.

    byte[] signedBytes = _signedHash;

 

    // Compute the hash message from the signed bytes.

    SHA256 hashstring = SHA256.Create();

    byte[] hash = hashstring.ComputeHash(signedBytes);

 

    // Send the signature hash to add a timestamp using an external service.

    var time = AddTimeStamp(hash);

    byte[] token = Base64.Decode(time.Token);

    timeStampResponse = token;

 

    return signedBytes;

}

 


Please follow the provided steps and implement them accordingly. If you encounter any issues or if the problem persists, kindly share your signing block information, complete code details, or the output timestamp document with us. This will allow us to further investigate and assist you accordingly.



JE Jeroen July 25, 2023 08:04 AM UTC

Hi


Sorry for the delayed response, I was on holiday for 2 weeks.

This also worked wonderfully.


My (hopefully) final issue is adding LTV.


I tried adding these lines to the empty signature:


signature.EnableLtv = true;
signature.CreateLongTermValidity(certificates);


But it doesn't seem to work. Also calling CreateLongTermValidity() seems to take a long time. Is this normal?




IJ Irfana Jaffer Sadhik Syncfusion Team July 26, 2023 12:15 PM UTC

I tried adding these lines to the empty signature:

signature.EnableLtv = true;

signature.CreateLongTermValidity(certificates);

But it doesn't seem to work.

To make the LTV (Long-Term Validation) feature work, we need to include information from the Certificate Revocation List (CRL) and the Online Certificate Status Protocol (OCSP) into the signed PDF. This information will be taken from the public root and intermediate certificates. If your certificates don't have this revocation information, the LTV won't be activated. Therefore, it's important to use certificates that have CRL/OCSP information to enable LTV.

 

Please refer to the below UG documentation about LTV,

https://help.syncfusion.com/file-formats/pdf/working-with-digitalsignature#create-long-term-validation-ltv-when-signing-pdf-documents-externally

 

Also calling CreateLongTermValidity() seems to take a long time. Is this normal?

It will take some time to receive a response regarding the revocation from the certificate revocation URI using an HTTP web request. Additionally, we kindly ask you to install your root & intermediate certificates in the Certificate Manager (certmgr) on your machine and then attempt to enable LongTermValidity.



JE Jeroen replied to Irfana Jaffer Sadhik July 31, 2023 03:28 PM UTC

In the example you gave, they're using the ComputeHash event method.

But I've been using the IPDFExternalSigner method (https://help.syncfusion.com/file-formats/pdf/working-with-digitalsignature#externally-sign-the-pdf-document-using-ipdfexternalsigner )

Is there any speci



RB Ravikumar Baladhandapani Syncfusion Team August 1, 2023 03:14 PM UTC


On our further analysis, we can Create LTV in external signature while signing the pdf document. Please refer the below documentation,

https://github.com/SyncfusionExamples/digitally-sign-pdf-csharp-examples/blob/master/Create_LTV_in_external_signature/Create_LTV_in_external_signature/Program.cs


Please let us know, if you need any further assistance on this.



JE Jeroen August 2, 2023 06:48 AM UTC

In the examples up until now, it was necessary to create an empty PDF signature first and then replace that empty signature. In this example the completed signature is added in one go. Is the first method no longer necessary?



RB Ravikumar Baladhandapani Syncfusion Team August 4, 2023 02:10 PM UTC

It is necessary to create an empty PDF signature first and then replace that empty signature because deferred signing is signing a document by creating a blank signature container, creating a signature appearance on the server, and getting a hash to send to the client. In real-time, the signed hash may be encoded with/without PKCS7 information. Therefore, we recommend creating an empty signature and utilizing our ReplaceEmptySignature functionality to accomplish this.



JE Jeroen replied to Ravikumar Baladhandapani August 4, 2023 02:35 PM UTC

So is LTV possible with deferred signing? I would think I need to call  CreateLongTermValidity() on the completed signature, but I don't have an instance of PdfSignature at that point.



RB Ravikumar Baladhandapani Syncfusion Team August 7, 2023 02:09 PM UTC

We can enable the LTV after completing the signature to load the signed PDF document and enable the LTV option or create Long-Term Validity for the existing signed signature field.

Please refer the below code snippet,

PdfLoadedDocument loadedDocument = new PdfLoadedDocument("LoadSignedPDF.pdf");

// Load the PDF document

PdfLoadedSignatureField signatureField = loadedDocument.Form.Fields[0] as PdfLoadedSignatureField;

signatureField.Signature.EnableLtv = true;

or

signatureField.Signature.CreateLongTermValidity(new List<X509Certificate2> {your root and intermediate certificates});


Please let us know, if you need any further assistance on this.



JE Jeroen September 7, 2023 09:54 AM UTC

I still can't get LTV to be added.


I have a signed pdf with embedded timestamp. I'm trying to execute the code snippet above (albeit with FileStreams since loading from string is not supported in the latest version).


But neither approach seems to be working. Yet opening the pdf in adobe and clicking "Add Verification information" seems to do the trick.


This is my code:

using (var stream = File.OpenRead(inPath))
{
PdfLoadedDocument loadedDocument = new PdfLoadedDocument(stream);
PdfLoadedSignatureField signatureField = loadedDocument.Form.Fields[0] as PdfLoadedSignatureField;


signatureField.Signature.EnableLtv = true;

 // or

signatureField.Signature.CreateLongTermValidity(BuildCertificateChain(cert));


using (var saveStream = File.Create(outPath))
{
loadedDocument.Save(saveStream);
}
}


where BuildCertificateChain calls X509Chain.Build() to build the entire certificate chain.

Any guidance?



RB Ravikumar Baladhandapani Syncfusion Team September 8, 2023 04:30 PM UTC

We tried to replicate the reported behavior with provided details on our end, but we are unable to reproduce this issue with our test documents. We suspect the reported issue occurs with a specific case. Could you please share the input pdf file, certificate and complete code snippet or simple sample to reproduce the issue, it will be helpful for our further investigation.



JE Jeroen September 11, 2023 11:34 AM UTC

Hi

I've created a ZIP with sample code and a signed pdf in the bin folder


Digidentity Test.zip





RB Ravikumar Baladhandapani Syncfusion Team September 12, 2023 04:03 PM UTC

Currently, we are analyzing the reported behavior with the provided details on our end and we will provide the further details on September 14th, 2023.



JE Jeroen September 15, 2023 06:55 AM UTC

Hi

Is there anything more you need from me to help your analysis?



RB Ravikumar Baladhandapani Syncfusion Team September 15, 2023 02:57 PM UTC

We were able to reproduce the reported behavior with provided details on our end. Currently, we are validating on this and will update the further details on September 21th, 2023.



JE Jeroen September 25, 2023 12:49 PM UTC

I see that a bug report was created for this issue.

Is there any other workaround I can use? Seeing as you didn't encounter the issue, maybe there's some other approach that does work?



RB Ravikumar Baladhandapani Syncfusion Team September 25, 2023 02:52 PM UTC

We confirmed the issue “Unable to enable LTV with signed PDF document” as a defect in our product and we will include the fix into our upcoming weekly NuGet release, which will be available on October 3rd, 2023.


Please use the below feedback link to track the status of the reported bug.

https://www.syncfusion.com/feedback/47057/unable-to-enable-ltv-with-signed-pdf-document


Note: If you require a patch for the reported issue in any of our Essential Studio Main or SP release version, then kindly let us know the version, so that we can provide a patch in that version based on our SLA policy.


Disclaimer: “Inclusion of this solution in the weekly release may change due to other factors including but not limited to QA checks and works reprioritization.”



RB Ravikumar Baladhandapani Syncfusion Team October 4, 2023 04:51 PM UTC

We have included the fix for this issue Unable to enable LTV with signed PDF document fix in our latest weekly release (23.1.39).Please use the below link to download our latest NuGet,

Nuget Link: NuGet Gallery | Syncfusion.Pdf.WinForms 23.1.39



JE Jeroen October 17, 2023 10:01 AM UTC

I'm also getting LTV enabled on my pdf now :)

Thanks very much.

The only complaint is that it takes a very long time right now in comparison to opening the pdf in Adobe Reader and adding the LTV info that way. Is this something that is improveable?




RB Ravikumar Baladhandapani Syncfusion Team October 19, 2023 05:21 AM UTC

The delay might be happening for other reasons, such as the revocation URI being unreachable or other network restrictions on our computer while checking the revocation in Adobe Reader. To improve this, you can open the PDF by disabling the signature verification option from the signature verification preference when the document is opened.


You can find this option by going to Edit, then Signature, then Verification, and finally Signature Verification Preferences.



Loader.
Up arrow icon