1

Thanks for your help,I have tested the solution suggested, but the problem is that when I call getrangestream(), a NullPointerException is raised.here is the code:

 public byte[] presign(string src,string dest){
 PdfReader reader=new PdfReader(src);  
 FileStream os=File.OpenWrite(dest);
 PdfStamper stamper = 
 PdfStamper.CreateSignature(reader, os, '\0'); 
 sap = stamper.SignatureAppearance; 
 Stream data=sap.GetRangeStream();
 hash = DigestAlgorithms.Digest(data, "SHA256");
 return hash;
 }//returns the hash to signing application on the server 
 public void postsign(byte[] signed_bytes){ 
IExternalSignature mysig=new MySignature(); 
 mysig.Sign(signed_bytes);//the signed_hash is returned from the server 
 MakeSignature.SignDetached(sap,mysig,final_chain,crlList,null, 
 null,8192,CryptoStandard.CMS)}

//the crllist and final_chain are "crl info" and "cert chain" info accordingly that are produced from reading a pem file

johny
  • 11
  • 3
  • You make two `MakeSignature.Sign*` calls for the same `appearance`. That does not make any sense. That being said, can you share a sample pdf signed by your code? – mkl Dec 16 '18 at 18:42
  • Actually, I have a client server program to sign a pdf.First I get the bytes to sign by calling getrangestream(), then I compute the hash, after that I send the hash to the signing application, finally I get the signed hash back to embed and sign my pdf. I was wondering if there is a straight forward way to do the job, because as it seems the above code does not work for me.@mkl – johny Dec 17 '18 at 07:13
  • *"I was wondering if there is a straight forward way to do the job,"* - yes, there is. Start signing the PDF only once. Use only one `MakeSignature.Sign*` call. In this call use a `IExternalSignature` or `IExternalSignatureContainer` impl in which the respective `Sign` method calls the signing application for the data from its argument and returns the result signature. — *"because as it seems the above code does not work for me"* - yes, that code is not straight forward, it starts signing twice, it finishes signing three times, it ignores `Sign` parameters and it doesn't call the signing app. – mkl Dec 17 '18 at 13:36
  • I have tested the solution suggested, but the problem is that when I call getrangestream(), a NullPointerException is raised.here is the code: – johny Dec 18 '18 at 08:36
  • public byte[] presign(string src,string dest){PdfReader reader=new PdfReader(src); FileStream os=File.OpenWrite(dest); PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0'); sap = stamper.SignatureAppearance; Stream data=sap.GetRangeStream();hash = DigestAlgorithms.Digest(data, "SHA256"); return hash;}//returns the hash to signing application on the server public void postsign(byte[] signed_bytes){IExternalSignature mysig=new MySignature();mysig.Sign(signed_bytes);//the signed_hash MakeSignature.SignDetached(sap,mysig,final_chain,crlList,null, null,8192,CryptoStandard.CMS)}@mkl – johny Dec 18 '18 at 08:52
  • Please edit your question to include the code. That much code in a comment is unreadable. – mkl Dec 18 '18 at 11:53
  • Thanks for your help, I have changed the question@mkl – johny Dec 18 '18 at 12:10
  • Ok, that code shows that you have not found the time yet to consider my previous comment in which I sketched what a "straight forward way" would look like. The current state of the iText signing APIs usually does not require you to explicitly use `GetRangeStream` anymore for a "straight forward" implementation of a signing use case (the underlying APIs will do that for you). – mkl Dec 18 '18 at 14:01

1 Answers1

0

The straight forward way to implement signing a PDF with iText 5.5.x using an external signing service or device is to use an IExternalSignature or IExternalSignatureContainer implementation in which the respective Sign method calls the external signing service or code to sign with that device for the data from its argument and returns the result signature.

For the sake of simplicity let's assume your signing service / device can be used to return a full-fledged CMS signature container. In that case one would use an IExternalSignatureContainer implementation like this:

PdfReader reader = new PdfReader(SRC);
FileStream os = new FileStream(DEST, FileMode.Create);
PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0');
// Creating the appearance
PdfSignatureAppearance appearance = stamper.SignatureAppearance;
appearance.Reason = "For a reason surely";
appearance.Location = "Positively somewhere";
appearance.SetVisibleSignature(new Rectangle(36, 748, 144, 780), 1, "sig");
IExternalSignatureContainer externalSignatureContainer = new ExternalServiceContainerSigner();
// Creating the signature
MakeSignature.SignExternalContainer(appearance, externalSignatureContainer, 8192);

with

class ExternalServiceContainerSigner : IExternalSignatureContainer
{
    public void ModifySigningDictionary(PdfDictionary signDic)
    {
        signDic.Put(PdfName.FILTER, PdfName.ADOBE_PPKLITE);
        signDic.Put(PdfName.SUBFILTER, PdfName.ADBE_PKCS7_DETACHED);
    }

    public byte[] Sign(Stream data)
    {
        String hashAlgorithm = "SHA256";
        byte[] hash = DigestAlgorithms.Digest(data, hashAlgorithm);
        // call your external signature service to create a CMS signature
        // container for the given document hash and return the bytes of
        // that signature container.
        return CALL_YOUR_EXTERNAL_SIGNATURE_SERVICE_TO_CREATE_A_CMS_SIGNATURE_CONTAINER_FOR(hash);
    }
}

If your signing service / device does not offer to create a CMS signature container but instead only naked signature bytes or a PKCS#1 style signature, you can either

  • replace the CALL_YOUR_EXTERNAL_SIGNATURE_SERVICE_TO_CREATE_A_CMS_SIGNATURE_CONTAINER_FOR call above by your own code preparing and signing a signature container for the given document hash using the external service / device or
  • use an IExternalSignature implementation calling your service and MakeSignature.SignDetached to use that implementation.
mkl
  • 90,588
  • 15
  • 125
  • 265
  • I'll appreciate your complete illustrative answer,It was useful and also straight forward, but I don't understand from where the parameter of sign method should be provided.I called getrangestream() to get the hashable bytes of the pdf document, so that to call sign method,but now what should I do to get the stream of hashable bytes?@mkl – johny Dec 19 '18 at 06:30
  • I have tried the code suggested in solution: https://stackoverflow.com/questions/49511383/signature-is-invalid-for-pdf-file-with-itext?noredirect=1&lq=1 , But I get the error :"The document has been altered or corrupted since the signature was applied". I have passed the GetByteToSign() return value as the data to sign. But it does not work. the hash algorithm is SHA256 and Encryption Algorithm is RSA.@mkl – johny Dec 19 '18 at 10:41
  • The signature area in the signed file includes the following data: >>>/ContactInfo()/M(D:20181219152353+03'30')/Filter/Adobe.PPKLite/SubFilter/adbe.pkcs7.detached/ByteRange [0 485 16871 74968] /Contents <308206d1fd84380c44adadb7ed56a1b0d2c371568e2613c3882d56df77b7681f5018382ed8158e2cc30a662e0c1bf864e000000000000000000000000000000000000000000000000000000.. – johny Dec 19 '18 at 12:08
  • The parameters set for signing the hash in the signing server is as follows: PKCS7_DETACHED,SHA256,RSA_PKCS and the base64 encoded string is sent to the server.@mkl – johny Dec 19 '18 at 12:59
  • *"I don't understand from where the parameter of sign method should be provided"* - which exactly? *"I have tried the code suggested in solution ..."* - The code there is not necessarily good code, it merely was easy to fix. – mkl Dec 19 '18 at 15:11
  • *"The parameters set for signing the hash in the signing server is as follows"* - I don't know your signing server, so those parameters mean nothing to me. But considering the data from the previous commentthe result from your call is not a full CMS container. You might want to investigate whether you can use different parameters to request a CMS signature container as answer from your server. Otherwise you have the options I listed at the bottom of my answer. – mkl Dec 19 '18 at 15:16
  • The problem is that I want to sign the hash of a pdf except for the signature container, First I compute the value by calling getrangestream and using a digest, say sha256.After the digest is passed to the signing service, wrong data is embedded in the generated pdf file.@mkl – johny Dec 22 '18 at 06:03
  • Also,the signature created is in pkcs#1 v.1.5 format, but I have defined the pkcs#7 detached signature to put in the pdf file@mkl – johny Dec 22 '18 at 09:02
  • sound silly to me,calling getrangestream() returns different values after each run, for the same file!I have no idea. Does the file change after each call?Or it works this way?@mkl – johny Dec 22 '18 at 13:27
  • *"sound silly to me,calling getrangestream() returns different values after each run, for the same file!I have no idea."* - If you did those calls for the same file after distinct `PdfStamper.CreateSignature` calls, that's obvious: different signature times, different file manipulation times, different file IDs... different hashes! – mkl Dec 23 '18 at 10:25
  • Thanks alot! Finally, It was a relief I completed the task.The problem was in the signing server as it did not interpret the hash value as HASH.I sent hashable stream as parameter to the signing server and it worked!@mkl – johny Dec 23 '18 at 10:49