|
//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); |
|
//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 |
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
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