Signing a PDF through iText sharp with the use of hash and signature from external web service

1.6k Views Asked by At

I am trying to sign a pdf through an external webservice. The web service receives a SHA-256 hex encoded hash and in when signed the service returns a hex digest signature.

At the moment i am stuck trying to use the iText 7 (c#) library trying to sign the document with the signature values. I did manage to sign a pdf document with itext with the use of a local selfsigned certificate but not with the external container yet, adobe acrobat always reports problems with the BER decoding which makes me to believe there is something going wrong either with the parsing on my side or the signature values are incorrect.

The signing steps with software samples are as followed:

  1. First add a blank dignature , sign this document and then generate a hash (SHA-256).

    public static void CalculateHash(Stream fileStream, out byte[] docBytesHash, out byte[] preSignedBytes)
    {
        PdfName filter = PdfName.Adobe_PPKLite;
        PdfName subFilter = PdfName.Adbe_pkcs7_detached;
        int estimatedSize = 8192;
        PdfReader reader = new PdfReader(fileStream);
        MemoryStream baos = new MemoryStream();
        PdfSigner signer = new PdfSigner(reader, baos, new StampingProperties());
        signer.SetCertificationLevel(PdfSigner.CERTIFIED_NO_CHANGES_ALLOWED);
        PdfSignatureAppearance appearance = signer.GetSignatureAppearance();
        appearance.SetLayer2Text("Signature field which signing is deferred.").SetPageRect(new Rectangle(36, 600,
            200, 100)).SetPageNumber(1);
        signer.SetFieldName("DeferredSignature1");
        DigestCalcBlankSigner external = new DigestCalcBlankSigner(filter, subFilter);
        //IExternalSignatureContainer external = new ExternalBlankSignatureContainer(filter, subFilter);
        signer.SignExternalContainer(external, estimatedSize);
    
        docBytesHash = external.GetDocBytesHash();
        preSignedBytes = baos.ToArray();
    }
    
  2. The hash is converted to bytearray and send to the webservice

    public static string ByteArrayToString(byte[] array)
    {
        StringBuilder stringBuilder = new StringBuilder();
    
        foreach (byte b in array)
            stringBuilder.AppendFormat("{0:x2}", b);
    
        return stringBuilder.ToString();
    }
    
  3. The webservice replies with the signature data an example of the string data

"473e8e376ca067f3c806902f718be21bf8a788ddbd31786b14fc47678596d6993a4f1ecb80e091f93af4820a75d97aee4b1a15c4a7914b4f881ca86e5d06b429b176d5b663c986c9ce2824333c98e0b5def0af53178b9ce38aa4efaa0adce2eee409487fb7fecf58e4c5bfcc3a0d083e35a83f9c722c73b78784e9990b6f00b89ae4934714c92b34699ce00ad5a662d0058bd613021449e9d09ab2d25376230de75591ab6ce4b5c5d24216794e8c871a690b4e19011621d41c66f4b0048abc9f2d4449072ee9e70c30dcf9b8b5a1ea8ee3a285163c2c5b293a3798a4a13ca59e83c66d9148d519b55e13643a3a7e0794732b92f50c1424f7be5774f67e910076"

  1. The string is then converted to bytearray with the following function

    public static byte[] StringToByteArray(String hex)
    {
        int NumberChars = hex.Length;
        byte[] bytes = new byte[NumberChars / 2];
        for (int i = 0; i < NumberChars; i += 2)
            bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
        return bytes;
    }
    
  2. The signature container is now constructed with the use of the byte array signature and the document is signed with the use of the container and the pre signed document bytes

    using (System.IO.FileStream outStream = new System.IO.FileStream(System.IO.Path.GetTempPath() + "tempout.pdf", System.IO.FileMode.Create))
    {
        PdfName filter = PdfName.Adobe_PPKLite;
        PdfName subFilter = PdfName.Adbe_pkcs7_detached;
    
        MemoryStream baos = new MemoryStream();
        fileStream.CopyTo(baos); //temp
    
        byte[] preSignedBytes = baos.ToArray();
    
    
    
        ReadySignatureSigner extSigContainer = new ReadySignatureSigner(StringHelper.StringToByteArray(signature.data.attributes.signature)); //now use external provider signature                                               
        PdfDocument docToSign = new PdfDocument(new PdfReader(new MemoryStream(preSignedBytes)));
        PdfSigner.SignDeferred(docToSign, "DeferredSignature1", outStream, extSigContainer);
        docToSign.Close();
    }
    

I also added the original pdf, the unsigned pdf and signed pdf for reference in below link. I hope someone can have a look at the contents and maybe tell me what is going wrong here. I already spend many hours online looking for solutions, but nothing found so far.

Regards, Jos Eilers

pdf's

Thanks to the answer of mkl i rewrote the code to build up the PKCS container as follows, first create the hash and presigned bytes and send this to the signing service :

        Org.BouncyCastle.X509.X509Certificate[] signChain = new Org.BouncyCastle.X509.X509Certificate[1];
        signChain[0] = Org.BouncyCastle.Security.DotNetUtilities.FromX509Certificate(signingcert);


        PdfName filter = PdfName.Adobe_PPKLite;
        PdfName subFilter = PdfName.Adbe_pkcs7_detached;
        int estimatedSize = 8192;
        PdfReader reader = new PdfReader(memoryStream);
        MemoryStream baos = new MemoryStream();
        PdfSigner signer = new PdfSigner(reader, baos, new StampingProperties());
        signer.SetCertificationLevel(PdfSigner.CERTIFIED_NO_CHANGES_ALLOWED);
        PdfSignatureAppearance appearance = signer.GetSignatureAppearance();
        appearance.SetLayer2Text("Signature field which signing is deferred.").SetPageRect(new Rectangle(36, 600,
            200, 100)).SetPageNumber(1);
        signer.SetFieldName("DeferredSignature1");
        DigestCalcBlankSigner external = new DigestCalcBlankSigner(filter, subFilter);

        signer.SignExternalContainer(external, estimatedSize);

        var signatureContainer = new PdfPKCS7(null, signChain, HASH_ALGORITHM, true);

        preSignedBytes = baos.ToArray();


        sh = signatureContainer.GetAuthenticatedAttributeBytes(preSignedBytes, null, null, CryptoStandard.CMS);

I then use the returned signature and create the new PKCS7 container and perform deferred signing as follows (hash mentioned is byte array sh from previous section) :

        var signatureContainer = new PdfPKCS7(null, signChain, HASH_ALGORITHM, true);

        signatureContainer.SetExternalDigest(StringHelper.StringToByteArray(signature.data.attributes.signature), null, "RSA");

        byte[] encodedSignature = signatureContainer.GetEncodedPKCS7(hash, null, null, null, CryptoStandard.CMS);
        ReadySignatureSigner extSigContainer = new ReadySignatureSigner(encodedSignature); //now use external provider signature
        memoryStream.Position = 0;
        PdfDocument docToSign = new PdfDocument(new PdfReader(memoryStream));

        MemoryStream memoryOutStream = new MemoryStream();
        PdfSigner.SignDeferred(docToSign, "DeferredSignature1", memoryOutStream, extSigContainer);

At the moment the certificate is visible in the pdf, but now i still have the error that the document has been altered.I am a little bit stuck at the moment, any suggestions? Also i am curious wether or not the use of a private key is required over here (i didnt get a reply yet from the provider if this is necessary).

Does anyone have any insights, the webservice used is from Digidentity

0

There are 0 best solutions below