1
votes

I try to digitally sign a pdf using an external web service. This web service contains the user certificate, which the user can access with their credentials and a one time password generated code.

Sidenote: The web service is supposed to expect the pdf digest (hash), but strangely it accepts the whole file instead.

Anyway the implementation is as follows:

using iTextSharp.text.pdf;
using iTextSharp.text.pdf.security;

// OTP = One Time Password code
public void SignPdf(string username, string password, string otp)
{
    byte[] file = GetFileFromPath("D:\test.pdf");
    var fieldName = "signatureField";
    string tempFilePath = "D:\test1temp.pdf");

    using (var pdfReader = new PdfReader(file))
    {
        using (var signedPdf = new FileStream(tempFilePath, FileMode.Create))
        {
            using (var pdfStamper = PdfStamper.CreateSignature(pdfReader, signedPdf, '\0', null, true);
            {
                // Prepare signature
                PdfDate date = new PdfDate();
                var fullSignDate = date.GetW3CDate();
                var signDate = fullSignDate.Substring(0, fullSignDate.IndexOf("T"));
                var signTime = fullSignDate.Substring(fullSignDate.IndexOf("T") + 1);
                var signatureAppearance = pdfStamper.SignatureAppearance;
                signatureAppearance.SetVisibleSignature(fieldName);
                signatureAppearance.Layer2Text=($"Digitally signed by: Test User");

                // Get font
                signatureAppearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.DESCRIPTION;
                signatureAppearance.CryptoDictionary = GetPdfSignature(signatureAppearance);
                signatureAppearance.PreClose(new Dictionary<PdfName, int> { [PdfName.CONTENTS] = 8192 * 2 + 2 });

                // Get file content as base64 string
                var ms = new MemoryStream();
                signatureAppearance.GetRangeStream().CopyTo(ms);
                fileAsBase64 = ms.ToArray();

                // Sign hash (the "hash" is the whole document)
                signature = GetSignedHash(Convert.ToBase64String(fileAsBase64), username, password, otp);

                if (signature != null)
                {
                    EmbedSignatureToPdf(signatureAppearance, signatureWithTimeStamp);
                    success = true;
                }
            }
        }
    }
}

private static PdfDictionary GetPdfSignature(PdfSignatureAppearance sa)
{
    return new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED)
    {
        Reason = sa.Reason,
        Location = sa.Location,
        SignatureCreator = sa.SignatureCreator,
        Contact = sa.Contact,
        Date = new PdfDate(sa.SignDate)
    };
}

private byte[] GetSignedHash(string hash, string username, string password, string otp)
{
    // Call external web service and get a the signed hash
}

private void EmbedSignatureToPdf(PdfSignatureAppearance signatureAppearance, byte[] signature)
{
    var array = new byte[8192];
    Array.Copy(signature, 0, array, 0, signature.Length);
    var pdfDictionary = new PdfDictionary();
    pdfDictionary.Put(PdfName.CONTENTS, new PdfString(array).SetHexWriting(true));
    signatureAppearance.Close(pdfDictionary);
}

The pdf is successfully signed, but using the Adobe Reader and checking the signature field it seems that a trusted timestamp is missing, as you can see on the picture:

digital signature without timestamp

It appears that the response from the remote service is a signature object that I just have to embed into the PDF file.

My question is: Since the signature that I receive does not contain a timestamp from a timestamp server, can I embed such a timestamp at this point or should the remote web service add it before sending the signature to me?

Thank you!

1
Such a time stamp is added to the signature container as unsigned attribute. Thus, you yourself can in particular add a time stamp to the signature container retrieved from your service. Depending on the nature of your signatures, though, you might have to increase the size of the placeholder in the pdf to have enough space for the additional time stamp.mkl
@mkl Thank you! I have done better research based on your answer and have found a way to include a timestamp using the "SecureBlackbox" software. Please add a real answer so I can accept it.iCantSeeSharp

1 Answers

1
votes

Since the signature that I receive does not contain a timestamp from a timestamp server, can I embed such a timestamp at this point or should the remote web service add it before sending the signature to me?

Such a time stamp is added to the signature container as unsigned attribute. Because they are not signed, unsigned attributes in a signature container can be changed after applying a signature, e.g. additional ones can de added.

Thus, you yourself can in particular add a time stamp to the signature container retrieved from your service.

Depending on the nature of your signatures, though, you might have to increase the size of the placeholder you create in the pdf to have enough space for the additional time stamp.