0

I want to place same externally signed hash (signature value) at multiple places in a PDF.

I have referred the page 'how-to-place-the-same-digital-signatures-to-multiple-places-in-pdf-using-itextsh' and tried to implement the work around provided by mkl (please refer this How to place the Same Digital signatures to Multiple places in PDF using itextsharp.net).

And it works. I ported it to get the signer bytes signed externally using web service/ api and it also works. Now due to one of the requirement I changed the way hash is being calculated.

now instead of (old one):

byte[] hash = DigestAlgorithms.Digest(data, "SHA256");
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);

I am trying to use (new one):

        int contentEstimated=8192;
        HashAlgorithm sha = new SHA256CryptoServiceProvider();
        int read = 0;
        byte[] buff = new byte[contentEstimated];
        while ((read = data.Read(buff, 0, contentEstimated)) > 0)
        {
            sha.TransformBlock(buff, 0, read, buff, 0);
        }
        sha.TransformFinalBlock(buff, 0, 0);
        byte[] hash = Org.BouncyCastle.Utilities.Encoders.Hex.Encode(sha.Hash);

        string hashtext = Encoding.UTF8.GetString(hash, 0, hash.Length); //for writing it to file or sharing it to another api
        byte[] hash1 = StringToByteArray(hashtext);

        byte[] sh = sgn.getAuthenticatedAttributeBytes(hash1, null, null, CryptoStandard.CMS); or 
        byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS); //tried both

and if I try to use it in existing implementation, the signature gets invalid with an error "Document has been altered or corrupt since it was signed". Can you please tell me where I am doing it wrong?

At most of the referred pages, they have used this hash generation method with an embed function where the calculated hash is getting embedded in the pdf,

    byte[] paddedSig = new byte[csize];
    System.Array.Copy(pk, 0, paddedSig, 0, pk.Length);
    PdfDictionary dic2 = new PdfDictionary();
    dic2.Put(PdfName.CONTENTS, new PdfString(paddedSig).SetHexWriting(true));
    appearance.Close(dic2);

Thank you. - Tanmay

Tanmay
  • 5
  • 4
  • It should work using just `sha.Hash`. – Paulo Soares Feb 23 '18 at 08:47
  • yes.. it worked with sha.hash. But if tried to get a hash string by sha.hash then it doesn't work. I tried.. string hashtext1 = Convert.ToBase64String(hash); string hashtext = Encoding.UTF8.GetString(hash, 0, hash.Length); With this I want to have a 64 bytes hash string. Is it possible? – Tanmay Feb 23 '18 at 09:15
  • The basic premise to change the implementation is to get 64 byte long hash.. I am looking for the ways to generate a document hash in a way, ----- 1 - to convert it to a 64 byte long string and 2 - use the hash byte array to get a valid signature in above implementation – Tanmay Feb 23 '18 at 09:41
  • 1
    You can't put anything you want in the hash, remember that the hash is recreated to be compared when the verification is done. If you need a 64 byte long hash try a SHA512. – Paulo Soares Feb 23 '18 at 14:25
  • As @Paulo said, the hash value parameter you put into `getAuthenticatedAttributeBytes` needs to be the result of applying the digest algorithm to the document data. You have the choice of digest algorithm, though, and SHA512 would result in a 64 byte value. Don't forget, though, to accordingly adapt the digest algorithm name you use in the `PdfPKCS7` constructor. On the other hand, if sha512 is not what you want, you probably should explain the background of your 64 bytes requirement... – mkl Feb 23 '18 at 17:37
  • I don't want to use sha512.. basically the document hash/ signer bytes are getting signed by 3rd party service, which requires **SHA256 hash value of the document in Hex format** – Tanmay Feb 28 '18 at 10:31
  • Is there any way to use `dic.Put(PdfName.CONTENTS, new PdfString(paddedSig).SetHexWriting(true));` in this implementation ?because it seems, to use hash in hex format, I have set the dictionary property.. Can anyone suggest any changes? – Tanmay Feb 28 '18 at 18:52
  • @mkl Any suggestions? Because with another method I can sign a single page with the signature returned by service (PKCS#7 / CMS signature), but then when I try to add that same signature at multiple places, I cannot do it. – Tanmay Mar 01 '18 at 05:28
  • All we can say seeing your attempt is that your code change makes no sense at all. You now say *"3rd party service, which requires SHA256 hash value of the document in Hex format"*. If it requires the *document* hash, I assume it returns a full-fledged CMS signature container. Does it? If otherwise it does not, it makes no sense for it to require the document hash; instead it should require the hash of the bytes to immediately, directly sign. Thus, please add backgrounds concerning that sign service to your question. – mkl Mar 01 '18 at 09:45
  • @mkl you are right. The service returns full-fledged CMS signature container (PKCS#7 / CMS signature). which I was to able to integrate for single page signing but not for multi-page. Actually I got to know the response format recently, that's why I was not able to mention it in the original question itself. My bad. – Tanmay Mar 01 '18 at 10:13
  • *"that's why I was not able to mention it in the original question itself"* - you can always edit your question to include additional information, there is an [edit](https://stackoverflow.com/posts/48943703/edit) link right underneath. But please refrain from replacing your question with a completely different one, at least if there already are answers to the original question, only add additional required information that way. – mkl Mar 01 '18 at 11:18
  • will keep that in mind.. Thanks – Tanmay Mar 01 '18 at 11:52

1 Answers1

0

In comments the OP clarified that he wants to adapt the solution here to using an external signing service which accepts a document hash (more exactly a SHA256 hash value of the document in Hex format) and returns a full-fledged CMS signature container.

In this case the original AllPagesSignatureContainer method Sign

public byte[] Sign(Stream data)
{
    String hashAlgorithm = externalSignature.GetHashAlgorithm();
    PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, false);
    IDigest messageDigest = DigestUtilities.GetDigest(hashAlgorithm);
    byte[] hash = DigestAlgorithms.Digest(data, hashAlgorithm);
    byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
    byte[] extSignature = externalSignature.Sign(sh);
    sgn.SetExternalDigest(extSignature, null, externalSignature.GetEncryptionAlgorithm());
    return sgn.GetEncodedPKCS7(hash, null, null, null, CryptoStandard.CMS);
}

has to be changed to not itself create a CMS container using the PdfPKCS7 sgn but merely calculate the document hash, send it to the service and use the container returned by the service:

public byte[] Sign(Stream data)
{
    String hashAlgorithm = externalSignature.GetHashAlgorithm();
    IDigest messageDigest = DigestUtilities.GetDigest(hashAlgorithm);
    byte[] hash = DigestAlgorithms.Digest(data, hashAlgorithm);
    byte[] hexHash = Org.BouncyCastle.Utilities.Encoders.Hex.Encode(hash);
    string hexHashString = Encoding.UTF8.GetString(hexHash , 0, hexHash.Length);
    var response = [... call service with document hash hexHashString ...];
    byte[] signatureContainer = [... extract binary CMS container from response ...];
    return signatureContainer;
}

The OP has not mentioned anything about the response format, so I cannot say more about the extract binary CMS container from response part. It may include selecting one attribute of a larger response structure, it may include decoding an encoded value (probably a Hex encoded string), ...

mkl
  • 90,588
  • 15
  • 125
  • 265
  • Needed to decode it and it worked. I was missing the point before returning the container, I was not removing the 'SetExternalDigest' statement. That's why I was getting corrupted signature. Thanks a ton. - Tanmay – Tanmay Mar 01 '18 at 11:55