PDF delay signing

Hi,
I would like to sign the pdf using a national id card from my web application.
Here is the process:

  1. Prepare the document for signing: add an empty signature field, get the hash of the PDF and return that hash to the client.

  2. Javascript client receives the hash and sends it to the native application that will do the signing

  3. Native application returns the signature of the hash and a certificate that was used for signing (without private key)

  4. Javascript client sends the signature and certificate to the server

  5. Server should open the prepared pdf and add the received signature to the pdf

The first problem that I have is with the step 1.
I was looking at the PdfDelayOrReSignDigitalId but in step 1 I don’t have a certificate so I tried this

    using (var document = GemBox.Pdf.PdfDocument.Load("SignDemo.pdf"))
    {
        // Add a visible signature field to the first page of the PDF document.
        var signatureField = document.Form.Fields.AddSignature(document.Pages[0], 300, 500, 250, 50);
        signatureField.Name = "gemsig";

        var sig = signatureField.Sign(SignerFunc, PdfSignatureFormat.PKCS7, 9000);

        document.Save("SignDemoGem_prepare.pdf");

       //return hash to the client
        return new
        {
            hash = MyPDFInfo.Hash
        };
    }

byte[] SignerFunc(Stream stream)
{
    //get pdf hash
    var hash = SHA256.Create().ComputeHash(stream);
    MyPDFInfo.Hash = Convert.ToHexString(hash);

    //return empty byte[]
    return new byte[8000];
}

I’m unsure if this is a good approach?

Next problem is with step 5.

 using (var document = GemBox.Pdf.PdfDocument.Load("SignDemoGem_prepare.pdf"))
 {
     // get signature field 
     var signatureField = document.Form.Fields.FirstOrDefault(x => x.Name == "gemsig");

     byte[] bytes = Convert.FromHexString(dto.certificate);
     var cert = new X509Certificate2(bytes);

     var pdfCertificate = new GemBox.Pdf.Security.PdfCertificate(cert);

     var digitalId = new PdfDelayOrReSignDigitalId(pdfCertificate);

     digitalId.Signature = Convert.FromHexString(dto.signature);

     var signer = new PdfSigner(digitalId);

     ((PdfSignatureField)signatureField).Sign(signer);
 }

This adds the signature but the signature is invalid. I get an error “Document has been altered or corrupted since it was signed”. I have noticed that the digitalId.Hash after calling Sign method is not the same as the one returned to the client

I have then tried another approach

using (var document = GemBox.Pdf.PdfDocument.Load("SignDemoGem_prepare.pdf"))
{
    // Add a visible signature field to the first page of the PDF document.
    var signatureField = document.Form.Fields.FirstOrDefault(x => x.Name == "gemsig");

    //dto.certificate
    //what to do with the certificate
    MyPDFInfo.Signature = dto.signature;

    ((PdfSignatureField)signatureField).Sign(SignerFunc2, PdfSignatureFormat.PKCS7, 9000);

    document.Save("SignDemoGem2_signed.pdf");

}

byte[] SignerFunc2(Stream stream)
{
    //get pdf hash
    //var hash = SHA256.Create().ComputeHash(stream);
    //the hash is the same as the one returned to the client
    
    //return the signature received from client
    
    return Convert.FromHexString(MyPDFInfo.Signature);
}

but I still get an invalid signature with error “There are errors in the formatting or information contained in this signature”

Can you please provide some guidance?
All the examples I have seen where using a certificate with private key or were calling an external web service in the same step.

Thank you

Hi,

We were unable to reproduce your issue.

One potential issue that I see is that you specified the estimated length to be 9000, but you return a byte array of length 8000 but this is probably not the cause of the issue.

Here is the sample code we used for testing:

static void Main()
{
    var hashHex = PrepareDocument();

    // Retrieve from the client.
    var signatureHex = SignHash(hashHex);
    var certificateHex = Convert.ToHexString(File.ReadAllBytes("GemBoxRSA1024.crt"));

    AddSignature(hashHex, signatureHex, certificateHex);
}

private static string PrepareDocument()
{
    using (var document = PdfDocument.Load("SignDemo.pdf"))
    {
        // Add a visible signature field to the first page of the PDF document.
        var signatureField = document.Form.Fields.AddSignature(document.Pages[0], 300, 500, 250, 50);
        signatureField.Name = "gemsig";

        string hashHex = null;
        var signatureContent = new byte[9000];
        var signature = signatureField.Sign(stream =>
        {
            using (var hashAlgorithm = SHA256.Create())
                hashHex = Convert.ToHexString(hashAlgorithm.ComputeHash(stream));
            return signatureContent;
        }, PdfSignatureFormat.PKCS7, signatureContent.Length);

        document.Save("SignDemoGem_prepare.pdf");

        if (Convert.ToHexString(signature.ComputeHash(PdfHashAlgorithm.SHA256)) != hashHex)
            throw new InvalidOperationException("Hash code not correctly calculated.");

        //return hash to the client
        return hashHex;
    }
}

private static string SignHash(string hashHex)
{
    using (var rsa = RSA.Create())
    {
        rsa.FromXmlString(File.ReadAllText("GemBoxRSA1024PrivateKey.xml"));
        return Convert.ToHexString(rsa.SignHash(Convert.FromHexString(hashHex), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
    }
}

private static void AddSignature(string hashHex, string signatureHex, string certificateHex)
{
    using (var document = PdfDocument.Load("SignDemoGem_prepare.pdf"))
    {
        // get signature field 
        var signatureField = document.Form.Fields.FirstOrDefault(x => x.Name == "gemsig");

        var pdfCertificate = new PdfCertificate(new X509Certificate2(Convert.FromHexString(certificateHex)));

        var digitalId = new PdfDelayOrReSignDigitalId(pdfCertificate);

        digitalId.Signature = Convert.FromHexString(signatureHex);

        var signer = new PdfSigner(digitalId);

        ((PdfSignatureField)signatureField).Sign(signer);

        if (Convert.ToHexString(digitalId.Hash) != hashHex)
            throw new InvalidOperationException("Hash code not correctly calculated.");
    }
}

The central part of the Main method is what you would retrieve from the client, but here we used the files from the external signature example to simulate this scenario.

The issue is probably with the client’s signing process or its signature format. Please make sure that the client signer uses SHA-256 hash and Pkcs1 signature padding.

If the issue still remains, send us the problematic signed PDF file and we will investigate the format of the signature (you can do this by submitting a ticket using the link on the page GemBox.Pdf Support - GemBox).

Regards,
Stipo

1 Like