0

I am trying to port a working solution for signing PDFs from a standard C# class library to portable class library (or windows store app 8.1). iTextSharp version: 5.5.3.0

The logic is as follows: I create a signature appearance in iTextSharp, hash it (SHA256, it's a third party requirement), send the hash to a webservice which returns me the signed content.

As mentioned, the solution is working fine e.g. in ASP.net web applications, but all the attempts to implement it in the WinRT environment seem to fail - the signature is applied, but it's invalid: message from PDF reader: "document has been altered or corrupted since the Signature was applied".

After analyzing the code differences, the only difference which seems relevant to me in this case is the hashing part. In the standard C# class library, I had it solved like that, and got it working with valid signatures as a result:

PdfSignatureAppearance sap = stp.SignatureAppearance;
// some appearance properties are filled here...

PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
dic.Date = new PdfDate(sap.SignDate);
dic.Reason = sap.Reason;
dic.Location = sap.Location;
sap.CryptoDictionary = dic;

Dictionary<PdfName, int> exc = new Dictionary<PdfName, int>();
exc.Add(PdfName.CONTENTS, csize * 2 + 2);
sap.PreClose(exc);

HashAlgorithm sha = new SHA256CryptoServiceProvider();
var sapStream = sap.GetRangeStream();
int read = 0;
byte[] buff = new byte[8192];
while ((read = sapStream.Read(buff, 0, 8192)) > 0)
{
    sha.TransformBlock(buff, 0, read, buff, 0);
}
sha.TransformFinalBlock(buff, 0, 0);

// here I am sending the hash to the third party webservice, 
// obtaining the 'signed' response
byte[] outc = new byte[csize];
PdfDictionary dic2 = new PdfDictionary();

Array.Copy(response, 0, outc, 0, response.Length);
dic2.Put(PdfName.CONTENTS, new PdfString(outc).SetHexWriting(true));               
sap.Close(dic2);

Since the libraries for WinRT are partly different, I try to implement the hashing using different class:

var sapStream = sap.GetRangeStream();
HashAlgorithmProvider alg = Windows.Security.Cryptography.Core.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256);
var hasher = alg.CreateHash();
int read = 0;
byte[] buff = new byte[8192];
while ((read = await sapStream.ReadAsync(buff, 0, 8192)) > 0)
{
   hasher.Append(buff.AsBuffer());
}

String hashText = CryptographicBuffer.EncodeToBase64String(hasher.GetValueAndReset());

Then I am sending the hashText to the webservice, obtain the response and put it into the file in the same manner, but the signature is invalid.

What am I missing?

lukasz
  • 311
  • 4
  • 17
  • 1
    *What am I missing?* - you ignore the `read` value. Especially the last block normally will not fully fill the `buff`, so your last `hasher.Append` call will hash the final block plus some trailing trash bytes which falsify your result. You may only hash the first `read` bytes of your `buff`. – mkl Nov 11 '14 at 16:25
  • Lukasz, could you at least take a look at your previous question (about the same topic) and clarify some of the confusion in there before continuing? – Maarten Bodewes Nov 11 '14 at 22:36
  • @mkl you're right! That was the point. Thank you very, very much! – lukasz Nov 12 '14 at 14:12
  • @owlstead Yes I will take a look and let you guys know, now that this case is solved, it helped me to understand the other problem better. Thank you for all your valuable help! – lukasz Nov 12 '14 at 14:13
  • @lukasz ok, I'll make the comment an answer to not leave your question dangling about seemingly unanswered. – mkl Nov 13 '14 at 08:13

1 Answers1

2

The issue in the WinRT version is that it ignores the read value in the hashing loop:

int read = 0;
byte[] buff = new byte[8192];
while ((read = await sapStream.ReadAsync(buff, 0, 8192)) > 0)
{
   hasher.Append(buff.AsBuffer());
}

Especially the last block normally will not fully fill the buffer buff, so the last hasher.Append call will hash the final block plus some trailing trash bytes which falsify the result.

You may only hash the first read bytes of your buff.

The OP eventually solved it like this:

while ((read = await sapStream.ReadAsync(buff, 0, 8192)) > 0)
{
    byte[] newArr = new byte[read];
    Array.Copy(buff, newArr, read);
    hasher.Append(newArr.AsBuffer());
}
mkl
  • 90,588
  • 15
  • 125
  • 265
  • Yes, and the working code could look like this: `while ((read = await sapStream.ReadAsync(buff, 0, 8192)) > 0) { byte[] newArr = new byte[read]; Array.Copy(buff, newArr, read); hasher.Append(newArr.AsBuffer()); })` Thanks a lot! – lukasz Nov 13 '14 at 08:23