Hello, my question is related to the same use case as this question.
I am trying to use PdfDelayOrReSignDigitalId class to get the hash that needs to be signed, sign the hash on the client, return the hash back to the server and use the same class to add the signed hash to the signature field. Here are the 2 methods in this process:
private static string GetHashTest(Guid documentId, int userId) {
ComponentInfo.SetLicense(Constants.GEMBOX_PDF_LICENSE);
Document doc = GetDocumentData(documentId, userId);
byte[] bytes = Convert.FromBase64String(doc.DocumentData);
var memoryStream = new MemoryStream();
memoryStream.Write(bytes, 0, bytes.Length);
GemBox.Pdf.PdfDocument pdfDoc = GemBox.Pdf.PdfDocument.Load(memoryStream);
PdfSignatureField signatureField = (PdfSignatureField)pdfDoc.Form.Fields["mds_signature1"];
Assembly assembly = Assembly.GetExecutingAssembly();
Stream crtStream = assembly.GetManifestResourceStream("Signatures.Signature.Keystore.mds.cer");
var crtMemStream = new MemoryStream();
crtStream.CopyTo(crtMemStream);
var crtBytes = crtMemStream.ToArray();
var pdfCertificate = new PdfCertificate(new X509Certificate2(crtBytes));
var digitalId = new PdfDelayOrReSignDigitalId(pdfCertificate);
signatureField.Sign(new PdfSigner(digitalId) { HashAlgorithm = PdfHashAlgorithm.SHA256, SignatureFormat = PdfSignatureFormat.CAdES, SignatureLevel = PdfSignatureLevel.PAdES_B_B });
using MemoryStream signedPdfStream = new MemoryStream();
pdfDoc.Save(signedPdfStream);
return Convert.ToBase64String(digitalId.Hash);
}
private static string SignHashTest(Guid documentId, int userId, string signedHash)
{
ComponentInfo.SetLicense(Constants.GEMBOX_PDF_LICENSE);
Document doc = GetDocumentData(documentId, userId);
byte[] bytes = Convert.FromBase64String(doc.DocumentData);
var memoryStream = new MemoryStream();
memoryStream.Write(bytes, 0, bytes.Length);
GemBox.Pdf.PdfDocument pdfDoc = GemBox.Pdf.PdfDocument.Load(memoryStream);
PdfSignatureField signatureField = (PdfSignatureField)pdfDoc.Form.Fields["mds_signature1"];
Assembly assembly = Assembly.GetExecutingAssembly();
Stream crtStream = assembly.GetManifestResourceStream("Signatures.Signature.Keystore.mds.cer");
var crtMemStream = new MemoryStream();
crtStream.CopyTo(crtMemStream);
var crtBytes = crtMemStream.ToArray();
var pdfCertificate = new PdfCertificate(new X509Certificate2(crtBytes));
var digitalId = new PdfDelayOrReSignDigitalId(pdfCertificate);
digitalId.Signature = Convert.FromBase64String(signedHash);
signatureField.Sign(new PdfSigner(digitalId) { HashAlgorithm = PdfHashAlgorithm.SHA256, SignatureFormat = PdfSignatureFormat.CAdES, SignatureLevel = PdfSignatureLevel.PAdES_B_B });
/*String pdfB64;
using (var pdfStream = new MemoryStream())
{
pdfDoc.Save(pdfStream);
pdfB64 = Convert.ToBase64String(pdfStream.ToArray());
}*/
return Convert.ToBase64String(digitalId.Hash);
}
I am trying to follow the process described here. Right now, the issue I have is that after adding the signedHash to the digitalId.Signature the digitalId.Hash stays null for some reason and I don’t know what I’m doing wrong here. Any help and guidance here is appreciated.
Hi mihmunta,
Your GetHashTest
method must also return the bytes of the delay-signed PDF file and you must store these bytes so that you can pass them to your SignHashTest
method instead of parameters documentId
and userId
.
By using the MemoryStream(byte[])
constructor in your SignHashTest
method to load the PdfDocument
, re-sign will update the byte array directly and after the re-sign the byte array will contain the signed PDF file.
Here is the updated code:
static void Main(string[] args)
{
var hash = GetHashTest(Guid.NewGuid(), Random.Shared.Next(), out byte[] signedDocumentBytes);
var signature = ClientSign(hash);
var hash2 = SignHashTest(signedDocumentBytes, signature);
File.WriteAllBytes("External Digital Signature.pdf", signedDocumentBytes);
}
private static string GetHashTest(Guid documentId, int userId, out byte[] signedDocumentBytes)
{
ComponentInfo.SetLicense(Constants.GEMBOX_PDF_LICENSE);
Document doc = GetDocumentData(documentId, userId);
byte[] bytes = Convert.FromBase64String(doc.DocumentData);
var memoryStream = new MemoryStream();
memoryStream.Write(bytes, 0, bytes.Length);
GemBox.Pdf.PdfDocument pdfDoc = GemBox.Pdf.PdfDocument.Load(memoryStream);
PdfSignatureField signatureField = (PdfSignatureField)pdfDoc.Form.Fields["mds_signature1"];
Assembly assembly = Assembly.GetExecutingAssembly();
Stream crtStream = assembly.GetManifestResourceStream("Signatures.Signature.Keystore.mds.cer");
var crtMemStream = new MemoryStream();
crtStream.CopyTo(crtMemStream);
var crtBytes = crtMemStream.ToArray();
var pdfCertificate = new PdfCertificate(new X509Certificate2(crtBytes));
var digitalId = new PdfDelayOrReSignDigitalId(pdfCertificate);
signatureField.Sign(new PdfSigner(digitalId) { HashAlgorithm = PdfHashAlgorithm.SHA256, SignatureFormat = PdfSignatureFormat.CAdES, SignatureLevel = PdfSignatureLevel.PAdES_B_B });
using MemoryStream signedPdfStream = new MemoryStream();
pdfDoc.Save(signedPdfStream);
signedDocumentBytes = signedPdfStream.ToArray();
return Convert.ToBase64String(digitalId.Hash);
}
private static string SignHashTest(byte[] signedDocumentBytes, string signedHash)
{
ComponentInfo.SetLicense(Constants.GEMBOX_PDF_LICENSE);
var memoryStream = new MemoryStream(signedDocumentBytes);
GemBox.Pdf.PdfDocument pdfDoc = GemBox.Pdf.PdfDocument.Load(memoryStream);
PdfSignatureField signatureField = (PdfSignatureField)pdfDoc.Form.Fields["mds_signature1"];
Assembly assembly = Assembly.GetExecutingAssembly();
Stream crtStream = assembly.GetManifestResourceStream("Signatures.Signature.Keystore.mds.cer");
var crtMemStream = new MemoryStream();
crtStream.CopyTo(crtMemStream);
var crtBytes = crtMemStream.ToArray();
var pdfCertificate = new PdfCertificate(new X509Certificate2(crtBytes));
var digitalId = new PdfDelayOrReSignDigitalId(pdfCertificate);
digitalId.Signature = Convert.FromBase64String(signedHash);
signatureField.Sign(new PdfSigner(digitalId) { HashAlgorithm = PdfHashAlgorithm.SHA256, SignatureFormat = PdfSignatureFormat.CAdES, SignatureLevel = PdfSignatureLevel.PAdES_B_B });
/*String pdfB64;
using (var pdfStream = new MemoryStream())
{
pdfDoc.Save(pdfStream);
pdfB64 = Convert.ToBase64String(pdfStream.ToArray());
}*/
return Convert.ToBase64String(digitalId.Hash);
}
I hope this helps!
Regards,
Stipo
Hi Stipo,
Thank you for your help. The hashes align now, but when I save the document and validate the signature in Acrobat I get the error that the document has been altered since the signature was applied.
After the signature is applied I still need to save the document and store it(even though the documentation for delay sign states that save should not be called) and I am not sure if that act of saving somehow corrupts the document.
That is the commented code in the above:
String pdfB64;
using (var pdfStream = new MemoryStream())
{
pdfDoc.Save(pdfStream);
pdfB64 = Convert.ToBase64String(pdfStream.ToArray());
}
Are there any other methods of saving a delay-signed document?
Hi mihmunta,
By using the MemoryStream(byte[])
constructor in your SignHashTest
method to load the PdfDocument
, re-sign will update the method parameter signedDocumentBytes
bytes directly and after the method returns, the signedDocumentBytes
will contain the signed PDF file.
So there is no need to save the PdfDocument in the re-sign method, since the bytes of the signed PdfDocument are already contained in the signedDocumentBytes
, but saving it to another Stream should execute without a problem and it will, effectively, just copy the bytes of the signedDocumentBytes
parameter to another Stream.
If you are still having issues, please submit a ticket with attached Visual Studio project that reproduces the issue and the invalid PDF file and we will investigate.
Regards,
Stipo