Prepare PDF hash to be signed and sign externally with smart card not on the same system, then append the signed hash to the PDF document

Hello,

Signing PDF documents with certificates present on the same system as the app works great. You can sign the document using a certificate file with a password or you can sign it using a smart card (non-exportable private key), where you are prompted to enter the PIN.

The problem arises when the signing process is separated between the back-end and front-end. Each user on the front-end should be able to independently sign PDF documents with their smart cards connected to their systems. The front-end is Angular + Electron, as such we are able to sign data using smart cards with node-webcrypto-p11.

Desired process:
1) Client requests hash of PDF to be signed from the back-end (.NET Core)
2) Hash should be constructed as such to include the empty signature field and everything needed to produce a valid signature - presumably not the whole PDF bytes, but the proper CMS structure
3) Returned hash is signed by Client using a smart card & PIN - resulting in a signed hash
4) Client posts the signed hash to the back-end
5) Signed hash is appended to the PDF document in the appropriate structure - resulting in a valid signature

The example provided by Dili Babu at How to add signed hash to pdf as signature is great but the resulting PDF contains an invalid signature. I think we're missing an essential part of the process.

If the process described above is possible, would you be able to provide an example? 

Thank you,
Marko

6 Replies 1 reply marked as answer

SL Sowmiya Loganathan Syncfusion Team December 1, 2020 11:20 AM UTC

Hi Marko,   
  
Thank you for contacting Syncfusion support.   
  
We have analyzed your requirement of external signing on different system and we can achieve this by using IPdfExternalSigner class. Please refer the below code snippet for more details,   
  
//Load PDF document  
PdfLoadedDocument loadedDocument = new PdfLoadedDocument(docStream);  
  
//Get the existing PDF page.  
PdfLoadedPage page = loadedDocument.Pages[0] as PdfLoadedPage;  
  
Syncfusion.Pdf.Security.PdfSignature signature = newSyncfusion.Pdf.Security.PdfSignature(loadedDocument, page, null, "Sig1");  
signature.Settings.CryptographicStandard = CryptographicStandard.CADES;  
signature.Settings.DigestAlgorithm = DigestAlgorithm.SHA1;  
//Create external signer  
IPdfExternalSigner externalSignature = new ExternalSigner("SHA1");  
  
//Please use your installed certificate thumprint  
X509Store store = new X509Store("MY", StoreLocation.CurrentUser);  
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);  
X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates;  
//Find the certificate using thumb print.  
X509Certificate2Collection fcollection = (X509Certificate2Collection)collection.Find(X509FindType.FindByThumbprint,"7402F6CD89410AA76816A1F0D82AC41923733B5B", false);  
X509Certificate2 digitalID = fcollection[0];  
  
List<X509Certificate2> certificates = new List<X509Certificate2>();  
certificates.Add(digitalID);  
signature.AddExternalSigner(externalSignature, certificates, null);  
signature.EnableLtv = true;  
  
//Save and close the document  
MemoryStream stream = new MemoryStream();  
loadedDocument.Save(stream);  
loadedDocument.Close(true);  
  
Sign method which returns the signed bytes (This will call while saving the PDF document):   
  
//Please use your side certification thumbprint and private key if condition  
public byte[] Sign(byte[] message, out byte[] timeStampResponse)  
{  
  
    X509Store store = new X509Store("MY", StoreLocation.CurrentUser);  
    store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);  
    X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates;  
    //Find the certificate using thumb print.  
    X509Certificate2Collection fcollection = (X509Certificate2Collection)collection.Find(X509FindType.FindByThumbprint,"7402F6CD89410AA76816A1F0D82AC41923733B5B", false);  
    X509Certificate2 digitalID = fcollection[0];  
  
    byte[] signedBytes = null;  
//This condition is based on the provided certificate private key  
    if (digitalID.PrivateKey is RSACryptoServiceProvider)  
    {  
        RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)digitalID.PrivateKey;  
        signedBytes = rsa.SignData(message, HashAlgorithm);  
    }  
    timeStampResponse = null;  
  
    return signedBytes;  
}  
  
  
  
Note: Please modify the above sample based on the installed certificate and private key  
  
Please try the above sample in your end and let us know if you need any further assistance on this.   
  
Regards,  
Sowmiya Loganathan  
 



MB Marko Bezjak December 4, 2020 05:03 AM UTC

Hello,

Thank you for your fast response and the provided example, which I have implemented and tested.

While it does give us the possibility of external signing with IPdfExternalSigner, where we can implement our own signing process, there is one essential part missing here, which is the preparation of the document before signing at a different time – not in the same session.

I would like to describe the process in a bit more detail.

It starts with the client requesting document bytes for signing from the server and also sends the certificate value, which is then used to create the X509Certificate2.

We get the bytes for signing by triggering the signing process (saving the document) as follows:

Syncfusion.Pdf.Security.PdfSignature signature = new Syncfusion.Pdf.Security.PdfSignature(loadedDocument, page, null, "Sig1");  

signature.Settings.CryptographicStandard = CryptographicStandard.CMS;  

signature.Settings.DigestAlgorithm = DigestAlgorithm.SHA256;  

IPdfExternalSigner externalSignature = new ExternalSigner("SHA256");

List certificates = new List();  

X509Certificate2 certificate = new X509Certificate2(Convert.FromBase64String(certificateValue));

certificates.Add(certificate);  

signature.AddExternalSigner(externalSignature, certificates, null);  


In the Sign method implementation, we save the message (bytes for signing) to a .txt file for later use. 

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

{

                timeStampResponse = null;            

                System.IO.File.WriteAllText("sig.txt", Convert.ToBase64String(message));                

                return message;

}


We then return the bytes to sign to the user as follows:

return Ok(System.IO.File.ReadAllText("sig.txt"));

- Here it is obvious, that we are missing a way to extract the bytes for signing from the prepared signature/document. This would simplify the above process, so it’s a method call instead of saving the bytes to a file, then reading from it (or saving to cache, etc.).


Client receives the bytes for signing and signs them using a smart card. We could also sign without a smart card, by certificates in user’s store, but that is not essential as the ending result is unfortunately the same.

Client then sends the signed message to the server.


This time we use a different implementation of IPdfExternalSigner’s Sign method as follows:

IPdfExternalSigner externalSignature = new FinalExternalSigner("SHA256", Convert.FromBase64String(signatureValue));

class FinalExternalSigner : IPdfExternalSigner

{

            private string _hashAlgorithm;

            public string HashAlgorithm => _hashAlgorithm;

            public byte[] _signedData;

            public FinalExternalSigner(string hashAlgorithm, byte[] signedData)

            {

                _hashAlgorithm = hashAlgorithm;

                _signedData = signedData;

            }

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

            {

                timeStampResponse = null;

                return _signedData;

            }

}


We save the document as in the example:

MemoryStream stream = new MemoryStream();  

loadedDocument.Save(stream);  

loadedDocument.Close(true);  

FileStream outStream = System.IO.File.OpenWrite("Output.pdf");

stream.WriteTo(outStream);

outStream.Flush();

stream.Dispose();


The resulting PDF (Output.pdf) properly contains the invisible signature with the certificate data, but the signature is invalid, and it says, “Document has been altered or corrupted since it was signed”.

It’s clear that we have a few missing pieces in this process.

Is there anything that we can implement differently to support this process?

Regards,
Marko



SL Sowmiya Loganathan Syncfusion Team December 7, 2020 12:54 PM UTC

Hi Marko,   
  
We have analyzed your requirement and currently we are validating to achieve this. We will update the further details on December 09, 2020.   
  
Regards,  
Sowmiya Loganathan 



GK Gowthamraj Kumar Syncfusion Team December 9, 2020 02:57 PM UTC

Hi Marko, 
 
Sorry for the inconvenience caused. 
  
We are still validating to achieve your requirement in our PDF library, currently, we are working on this with high priority and we will update the further details on December 11, 2020 without any further delay. 

Regards, 
Gowthamraj K 



GK Gowthamraj Kumar Syncfusion Team December 11, 2020 01:23 PM UTC

Hi Marko, 
 
At present, we do not have support for “Deferred signing in PDF document” . We have logged a feature request for deferred signing in the PDF document in our PDF library and  we will implement this support in our upcoming 2021 Volume 1 release on March tentatively . 
 
Please find the feedback link to track the implementation of the feature below.    
 
Regards, 
Gowthamraj K 



GK Gowthamraj Kumar Syncfusion Team March 31, 2021 11:52 AM UTC

Hi Marko, 
 
Thank you for your patience. 
 
We have included the feature “Support for deferred signing in PDF document in our Essential Studio 2021 Volume 1 main release v19.1.0.54. 
 
The status of this feature can be tracked through the following feedback link,     
 
We are glad to announce that our Essential Studio 2021 Volume 1 Main release v19.1.0.54 is rolled out and is available for download under the following link. 
 
We thank you for your support and appreciate your patience in waiting for this release. Please get in touch with us if you would require any further assistance. 

Regards, 
Gowthamraj K 


Marked as answer
Loader.
Up arrow icon