BoldDesk®Customer service software offering ticketing, live chat, and omnichannel support, starting at $49/mo. for 10 agents. Try it for free.
First of all i really like the simplicity of Syncfusion Controls, but currently i have a rather complex scenario to deal with.
Exposing the case: i have am external service that i need to send the DTBS portion of the PDF and hash it to SHA256, then external service returns the public certificates chain (SIGN, ROOT, CA), Note i don't have any pfx or private key, only the return of a signed hash from the service and public certificates chain, do you support this scenario?
Unfortunately for me is not very clear how to anchieve this based on the documentation provided Working with Digital Signature | Syncfusion.
I do have a working sample from another PDF library (Itext) that i found could be done with , code below as follows:
publicstaticvoidPreparePdfForSignage()
{
byte[] pdf =System.IO.File.ReadAllBytes("../original.pdf");
string certificate =Convert.ToBase64String(System.IO.File.ReadAllBytes("../Certificate.cer"));
string digestAlgorithmOid ="2.16.840.1.101.3.4.2.1";
var fieldName ="Signature1";
byte[] preparedBytes;
CMSContainer cmsContainer;
usingMemoryStream unsignePdfStream =new(pdf);
usingMemoryStream preparedPdfStream =new();
using(PdfReader reader =newPdfReader(unsignePdfStream))
using(MemoryStream outputStream = preparedPdfStream)
{
var parser =BcFactory.CreateX509CertificateParser();
IX509Certificate[] certificateChain =[.. parser.ReadAllCerts(Convert.FromBase64String(certificate))];
string digestAlgorithm =DigestAlgorithms.GetDigest(digestAlgorithmOid);
// 1. Preparing the container to get a size estimate
cmsContainer =newCMSContainer();
cmsContainer.AddCertificates(certificateChain);
cmsContainer.GetSignerInfo()
.SetSigningCertificateAndAddToSignedAttributes(certificateChain[0], digestAlgorithmOid);
// In a later version the default algorithm is extracted from the certificate
var algorithmIdentifier =GetAlgorithmOidFromCertificate(certificateChain[0]);
cmsContainer.GetSignerInfo().SetSignatureAlgorithm(algorithmIdentifier);
cmsContainer.GetSignerInfo().SetDigestAlgorithm(new iText.Signatures.Cms.AlgorithmIdentifier(digestAlgorithmOid));
// Get the estimated size
long estimatedSize = cmsContainer.GetSizeEstimation();
var digest =BcFactory.CreateIDigest(digestAlgorithm);
// Add enough space for the digest
estimatedSize += digest.GetDigestLength()*2L+2;
// Duplicate the size for conversion to hex
estimatedSize *=2;
PdfTwoPhaseSigner signer =newPdfTwoPhaseSigner(reader, outputStream);
signer.SetStampingProperties(newStampingProperties().UseAppendMode());
// 2. Prepare the document by adding the signature field and getting the digest in return
byte[] documentDigest = signer.PrepareDocumentForSignature(signerProperties, digestAlgorithm,
PdfName.Adobe_PPKLite,PdfName.Adbe_pkcs7_detached,(int)estimatedSize,true);
// 3. Add the digest to the CMS container, because this will be part of the items to be signed
cmsContainer.GetSignerInfo().SetMessageDigest(documentDigest);
outputStream.Close();
preparedBytes = outputStream.ToArray();
}
// 4. This step is completely optional. Add the CMS container to the document
// to avoid having to build it again, or storing it separately from the document
using(PdfReader reader =newPdfReader(newMemoryStream(preparedBytes)))
using(PdfDocument document =newPdfDocument(reader))
using(MemoryStream outputStream =newMemoryStream())
{
PdfTwoPhaseSigner.AddSignatureToPreparedDocument(document, fieldName, outputStream,
cmsContainer.Serialize());
outputStream.Close();
varPreparedFile= outputStream.ToArray();
varBytesToSign= cmsContainer.GetSerializedSignedAttributes();
// 5. The serialized signed attributes is what actually needs to be signed
// sometimes we have to create a digest from it, sometimes this needs to be sent as is.
using(System.IO.FileStream fs =System.IO.File.Create("../PreparedFile.pdf"))
{
fs.Write(PreparedFile,0,PreparedFile.Length);
}
// BytesToSign are the byte[] array needed to be sent to externa service, that i will hash in SHA256
using(System.IO.FileStream fs =System.IO.File.Create("../DTBSbytestosign"))
{
fs.Write(BytesToSign,0,BytesToSign.Length);
}
}
}
Then i will reread document and sign, adding the signed hash to the cms container.
public static void SignPreparedPdf() { byte[] preparedPdf = System.IO.File.ReadAllBytes("../PreparedFile.pdf"); string fieldName = "Signature1"; byte[] signature = System.IO.File.ReadAllBytes("../SignedHashBytes"); using (PdfReader reader = new PdfReader((new MemoryStream(preparedPdf)))) using (PdfDocument document = new PdfDocument(reader)) using (MemoryStream outputStream = new MemoryStream()) { // 1. Read the documents CMS container SignatureUtil su = new SignatureUtil(document); PdfSignature sig = su.GetSignature(fieldName); PdfString encodedCMS = sig.GetContents(); byte[] encodedCMSdata = encodedCMS.GetValueBytes(); CMSContainer cmsContainer = new CMSContainer(encodedCMSdata); // 2. Add the signatureValue to the CMS cmsContainer.GetSignerInfo().SetSignature(signature); PdfTwoPhaseSigner.AddSignatureToPreparedDocument(document, fieldName, outputStream, cmsContainer.Serialize()); outputStream.Close(); using (System.IO.FileStream fs = System.IO.File.Create("../FinalSigned.pdf")) { fs.Write(outputStream.ToArray(), 0, outputStream.ToArray().Length); } }
So would be very appreciated if i could be pointed out how to anchieve the same result in Syncfusion PDF Library if possible!
Hi Nuno,
Thank you for your positive feedback on Syncfusion controls. Regarding your complex scenario involving external digital signatures, Syncfusion's .NET PDF Library does support external signing processes similar to the one you've implemented with iText.
Our library allows you to sign PDF documents using external digital signatures created from various sources, such as Hardware Security Modules (HSM), USB tokens, smart cards, or other cloud services:
How digitally sign a PDF document using the USB token in WF application?
For detailed guidance on implementing external signatures, you can refer to our knowledge base article:
Additionally, we have a GitHub repository with examples demonstrating how to digitally sign PDFs using C#, including scenarios with external signatures:
These resources should help you achieve the desired functionality using Syncfusion's PDF Library.Please try this on your end and let us know the feedback.
Regards,
Irfana J
Thank you for the fast answer, i will check it out and try to implement it and give feedback afterward.
I gave a look at some samples and maybe i am missing something ,but still can't find something related to the sample i provided.
Please note that i would need a 2 phase process in the sense that i would need to create the pdf with the signing appearance etc and create the signing field and only add the public certificate chain at my disposal.
Then i need to send the signing DTBS portion of the signature field to be signed, so that in a 2nd phase i can reopen the pdf and sign it with the hash provided by the external service.
Would be of great help if ir could be provided a working sample close to what i have done in iText , Unfortunately on all your samples i still don't see a closed enough sample.
With your samples i could easily with sign a pdf using a pfx certificate that has a private key, but as i mentioned this is not the case or scenario i am looking for.
Looking fforward if possible for a close sample to what i provided on this thread . Would be amazingly helpfull.
Thanks.
HI Nuno,
We have prepared a sample to achieve your requirement. Please try this on your end and let us know the result.
How this relates to your scenario:
This sample closely mirrors the iText 2-phase deferred signing approach.
Regards,
Irfana J.
Hi, thanks again for the quick reply and the example, i did try this approach but with no luck , what is confusing for me is the fact that as i explained i dont have a Private Key on the certificate neither is a pfx, so the method to sign the Hash on The SignEmpty class will never return the byte[] array to sign.
// Load the PFX file containing the private key. Here we are using pfx file for demonstrate this. X509Certificate2 digitalID = new X509Certificate2(Path.GetFullPath(@"../../../Data/PDF.pfx"), "password123"); // Sign the document hash using the available RSA provider if (digitalID.PrivateKey is RSACryptoServiceProvider rsaProvider) { Program.SignedHash = rsaProvider.SignData(documentHash, HashAlgorithm); } else if (digitalID.PrivateKey is RSACng rsaCng) { Program.SignedHash = rsaCng.SignData(documentHash, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1); } else if (digitalID.PrivateKey is RSAOpenSsl rsaOpenSsl) { Program.SignedHash = rsaOpenSsl.SignData(documentHash, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1); }
But i guess i will have to try and get some info without the PrivateKey and check it out.
Thanks for all info.
Hi Nuno,
We have used the private key in the signing part of the application solely for demonstration purposes. You can send the documentHash
received from the SignEmpty
external class to your service provider to obtain the signed hash. Therefore, you can ignore the .pfx
file, as it is only included to complete this sample.
In this sample, only public certificates are used for creating the signature and its appearance.
IPdfExternalSigner
(SignEmpty
) class.SignEmpty
class for future signing.SignEmpty
class and get the signed hash.IPdfExternalSigner
to return the signed hash.Please let us know if you need any further details. We’ll be happy to assist you.
Regards,
Irfana J.