0
votes

I am using iText7 for performing PDF and signing operations. My scenario is I am computing hash on my local machine and sending this hash to signing server and in response get the signed PKCS1(Raw signature) and then I am embedding this signature into PDF. My code snippet is as follows:

1: Read public cert from smart card device.

2: Initialize PdfReader from original document bytes containing signature field named "Signature1"

3: Initialize PdfSigner and set signature appearance:

PdfSigner pdfSigner = new PdfSigner(pdfReader, outputStream, new StampingProperties().UseAppendMode());
 pdfSigner.SetFieldName("Signature1");
 pdfSigner.GetDocument().GetCatalog().SetModified();
ImageData imageData = ImageDataFactory.Create(handSignatureBytes);
PdfSignatureAppearance signatureAppearance = pdfSigner.GetSignatureAppearance();
 signatureAppearance.SetContact("contactInfo");
 signatureAppearance.SetLocation("locationInfo");
 signatureAppearance.SetPageNumber(1);
 signatureAppearance.SetReason("signingReason");
 signatureAppearance.SetSignatureGraphic(imageData);
 signatureAppearance.SetRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC);
 signatureAppearance.SetSignatureCreator("Malik");
 signatureAppearance.SetCertificate(x509Certificate);

4: I have implemented IExternalSignatureContainer interface to get document hash:

public class PreSigning : IExternalSignatureContainer
 {
 protected PdfDictionary sigDic;
 private byte[] hash;
public PreSigning(PdfName filter, PdfName subFilter)
 {
 sigDic = new PdfDictionary();
 sigDic.Put(PdfName.Filter, filter);
 sigDic.Put(PdfName.SubFilter, subFilter);
 }
public void ModifySigningDictionary(PdfDictionary signDic)
 {
 signDic.PutAll(sigDic);
 }
public byte[] Sign(Stream data)
 {
 this.hash = DigestAlgorithms.Digest(data, DigestAlgorithms.GetMessageDigest("SHA256"));
 return new byte[0];
 }
public byte[] getHash()
 {
 return hash;
 }
public void setHash(byte[] hash)
 {
 this.hash = hash;
 }
 }

5: Getting document hash:

PreSigning external = new PreSigning(PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached);
 pdfSigner.SignExternalContainer(external, estimatedSize);
byte[] documentHash = external.getHash();

6: Initialize PdfPKCS7 class to get Data To Be Signed and getting hash of Data To Be Signed to send to signing server:

PdfPKCS7 pdfPKCS7 = new PdfPKCS7(null, x509CertificatesChain, "SHA256", false);
 dataToBeSigned = pdfPKCS7.GetAuthenticatedAttributeBytes(documentHash, PdfSigner.CryptoStandard.CMS, null, null);
byte[] dataToSignHash = DigestAlgorithms.Digest(new MemoryStream(dataToBeSigned), DigestAlgorithms.GetMessageDigest("SHA256"));

7: I have keep the outputStream from PdfSigner for signature embedding phase:

documentStreamBytes = ((MemoryStream)outputStream).ToArray();

8: Send the Data To Be Signed hash to signing server.

9: Get the PKCS1 data from signing server in response:

byte[] PKCS1 = Convert.FromBase64String(preSigningResponse.signedHash);

10: Initialized PdfPKCS7 class for getting PKCS7 from PKCS1:

PdfPKCS7 pdf = new PdfPKCS7(null, x509CertificatesChain, "SHA256", false);
pdf.SetExternalDigest(PKCS1, null, "RSA");
byte[] pkcs7Data = pdf.GetEncodedPKCS7(documentHash, PdfSigner.CryptoStandard.CMS, null, null, null);

11: Get the original document from documentStreamBytes:

Stream pdfReaderStream = new MemoryStream(documentStreamBytes);
PdfReader reader = new PdfReader(pdfReaderStream);
PdfDocument originalDocument = new PdfDocument(reader, new PdfWriter(new MemoryStream()));

12: I have implemented IExternalSignatureContainer for signature embedding using PdfSigner.SignDeferred() method:

public class PostSigning : IExternalSignatureContainer
 {
 protected byte[] _sig;
 public PostSigning (byte[] sign)
 {
 _sig = sign;
 }
 public void ModifySigningDictionary(PdfDictionary signDic)
 {
 }
public byte[] Sign(Stream data)
 {
 return _sig;
 }
 }

13: Calling PdfSigner.SignDeferred() method to get the final document:

Stream resultStream = new MemoryStream();
IExternalSignatureContainer externalSignatureContainer = new PostSigning(pkcs7Data);
PdfSigner.SignDeferred(originalDocument, "Signature1", resultStream, externalSignatureContainer);
byte[] finalDoc = ((MemoryStream)resultStream).ToArray();

I am getting the following error: Document has been altered or corrupted since the signature has been applied.

Can anyone help me regarding this scenario using iText7

1
Are you sure the signerCert includes a private key?mkl
@mkl No the private key is in the token which sign the hash and return pkcs1. Signer Cert is the public keyMuddassir Awan
Ah, ok, I've been looking at your coffee some more. There are other issues. I'll answer later.mkl
@mkl ok waiting for your answerMuddassir Awan
Did my answer clear up the issue?mkl

1 Answers

1
votes

There are numerous issues in your code.

Starting a signature twice

Foremost: In your second part (Embed the returned PKCS1 into PDF) you start again with the original file and create a new signature field therein. This results in a slightly different PDF than the one you prepared in your first part; thus, the signature value you have retrieved between the two parts obviously cannot be used for this new PDF.

You must change your architecture.

If you want to keep your two-step approach, you have to keep the file you created in your first part in outputStream and re-use it in the second part. And to fill in the retrieved signature, you must use PdfSigner.signDeferred instead of starting with a new PdfSigner.

Alternatively you can do it in one step, putting the signature server call into a custom IExternalSignatureContainer implementation.

Signing the wrong PDF data

In your first part you appear to set the outputStream contents as the data to sign (actually you even put it into a field that by its name, responseObject.base64Hash, should only hold a hash value; I don't understand that at all). But the bytes to sign are not the complete outputStream contents, a placeholder therein for the final signature value must be excluded.

You must only sign the resulting PDF without that placeholder. You can get it if instead of the ExternalBlankSignatureContainer as is you extend it by overriding the byte[] Sign(Stream data) method and grabbing the Stream parameter thereof. This stream contains exactly the bytes to sign.

The wrong signature format

You mention you get the signed Pkcs1 from your signing server. What you need, though, is a CMS signature container.

If your signature server also offers to return CMS (or PKCS#7) signature containers, use that kind of call.

Otherwise you must build a CMS container yourself, e.g. using the iText PdfPKCS7 class or BouncyCastle mechanisms.