signmanual (signmanual.cpp) – this example shows how to digitally sign a PDF document, using Crypto API with a certificate stored in a .pfx file. The first part defines a simple signer class, MPdfSigner, which is responsible for signature operations and calls to Crypto API. The main part of the example creates a new document and signs it with help of the MPdfSigner, both to a file and to a memory buffer. The example shows how to add a visual annotation with an information about the signature on the second page of the document. It also signs already signed document, using the incremental update. Note that a page changes invalidate any previous signatures.

    1 /*
    2  * (c) 2013-2017 http://www.litePDF.cz
    3  *
    4  * The example code is supplied "AS IS". It disclaims all warranties, expressed
    5  * or implied, including, without limitation, the warranties of merchantability
    6  * and of fitness for any purpose. It assumes no liability for direct, indirect,
    7  * incidental, special, exemplary, or consequential damages, which may result
    8  * from the use of the code, even if advised of the possibility of such damage.
    9  *
   10  * Permission is hereby granted to use, copy, modify, and distribute this
   11  * source code, or portions hereof, for any purpose, without fee.
   12  */ 
   13 
   14 #include <windows.h>
   15 #include <wincrypt.h>
   16 #include <stdio.h>
   17 #include <string.h>
   18 #include <string>
   19 #include <time.h>
   20 
   21 #include "share/litePDF.h"
   22 
   23 // Link with the crypt32.lib file
   24 #pragma comment (lib, "crypt32.lib")
   25 
   26 static std::string to_string(unsigned int num)
   27 {
   28    char buff[128];
   29 
   30    if (num == (unsigned int) -1) {
   31       return std::string("-1");
   32    }
   33 
   34    sprintf(buff, "%u", num);
   35 
   36    return std::string(buff);
   37 }
   38 
   39 class MPdfSigner
   40 {
   41  private:
   42    unsigned int lastSignatureIndex;
   43 
   44    static void __stdcall appendSignatureData(const char *bytes,
   45                                              unsigned int bytes_len,
   46                                              void *user_data)
   47    {
   48       MPdfSigner *signer = (MPdfSigner *) user_data;
   49 
   50       if (bytes && bytes_len) {
   51          signer->AddData(bytes, bytes_len);
   52       }
   53    }
   54 
   55    static void __stdcall finishSignature(char *signature,
   56                                          unsigned int *signature_len,
   57                                          void *user_data)
   58    {
   59       MPdfSigner *signer = (MPdfSigner *) user_data;
   60       signer->Finish(signature, signature_len);
   61    }
   62 
   63    void loadCertificateStore(HANDLE *hStore)
   64    {
   65       BYTE *certBytes = NULL;
   66       unsigned int certBytesLength = 0;
   67 
   68       FILE *certFile = fopen("cert.pfx", "rb");
   69       if (!certFile) {
   70          throw TLitePDFException(ERROR_FILE_NOT_FOUND,
   71                                  "Failed to open 'cert.pfx'");
   72       }
   73 
   74       fseek(certFile, 0, SEEK_END);
   75       certBytesLength = ftell(certFile);
   76       fseek(certFile, 0, SEEK_SET);
   77       certBytes = (BYTE *) malloc(sizeof(BYTE) * certBytesLength);
   78       if (!certBytes) {
   79          throw TLitePDFException(ERROR_OUTOFMEMORY,
   80                                  "Failed to allocate memory for cert.pfx");
   81       }
   82 
   83       fread(certBytes, sizeof(BYTE), certBytesLength, certFile);
   84 
   85       fclose(certFile);
   86 
   87       HMODULE lib = LoadLibrary("crypt32.dll");
   88       if (lib != NULL) {
   89          /*
   90             PFXData...
   91          */
   92          typedef HCERTSTORE (WINAPI *LPPFXImportCertStore) (CRYPT_DATA_BLOB *pPFX,
   93                                                             LPCWSTR szPassword,
   94                                                             DWORD dwFlags);
   95          typedef BOOL (WINAPI *LPPFXIsPFXBlob)(CRYPT_DATA_BLOB *pPFX);
   96          typedef BOOL (WINAPI *LPPFXVerifyPassword)(CRYPT_DATA_BLOB *pPFX,
   97                                                     LPCWSTR szPassword,
   98                                                     DWORD dwFlags);
   99          #ifndef CRYPT_USER_KEYSET
  100          #define CRYPT_USER_KEYSET         0x00001000     
  101          #endif
  102 
  103          LPPFXImportCertStore libPFXImportCertStore =
  104                (LPPFXImportCertStore) GetProcAddress(lib,"PFXImportCertStore");
  105          LPPFXIsPFXBlob libPFXIsPFXBlob =
  106                (LPPFXIsPFXBlob) GetProcAddress(lib, "PFXIsPFXBlob");
  107          LPPFXVerifyPassword libPFXVerifyPassword =
  108                (LPPFXVerifyPassword) GetProcAddress(lib,"PFXVerifyPassword");
  109          if (libPFXImportCertStore != NULL &&
  110              libPFXIsPFXBlob != NULL &&
  111              libPFXVerifyPassword != NULL) {
  112             CRYPT_DATA_BLOB blob;
  113             blob.cbData = certBytesLength;
  114             blob.pbData = certBytes;
  115             if (libPFXIsPFXBlob(&blob)) {
  116                *hStore = libPFXImportCertStore(&blob, L"",
  117                      0/*|CRYPT_USER_PROTECTED|CRYPT_USER_KEYSET*/);
  118             }
  119          }
  120          FreeLibrary(lib);
  121       }
  122 
  123       free(certBytes);
  124    }
  125 
  126    char *bytes;
  127    int bytes_len;
  128 
  129    void AddData(const char *pBytes, unsigned int pBytes_len)
  130    {
  131       bytes = (char *) realloc(bytes, sizeof(char) * (bytes_len + pBytes_len));
  132       if (!bytes) {
  133          std::string msg = "Failed to allocate " +
  134                            to_string(bytes_len + pBytes_len) + " bytes";
  135          throw TLitePDFException(ERROR_OUTOFMEMORY, msg.c_str());
  136       }
  137 
  138       memcpy(bytes + bytes_len, pBytes, pBytes_len);
  139       bytes_len += pBytes_len;
  140    }
  141 
  142    void Finish(char *signature, unsigned int *signature_len)
  143    {
  144       HANDLE hStore = NULL;
  145       PCCERT_CONTEXT pCertContext = NULL;
  146       const BYTE* MessageArray[] = {(const BYTE *) bytes};
  147       DWORD MessageSizeArray[1];
  148       MessageSizeArray[0] = bytes_len;
  149       CRYPT_SIGN_MESSAGE_PARA  SigParams;
  150 
  151       loadCertificateStore(&hStore);
  152       if (!hStore) {
  153          throw TLitePDFException(GetLastError(),
  154                                  "Failed to open a temporary store");
  155       }
  156 
  157       pCertContext = CertFindCertificateInStore(hStore,
  158             PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, CERT_FIND_ANY,
  159             NULL, NULL);
  160       if (!pCertContext) {
  161          CertCloseStore(hStore, 0);
  162          throw TLitePDFException(GetLastError(),
  163                                  "Failed to find certificate in the store");
  164       }
  165 
  166       memset(&SigParams,0,sizeof(CRYPT_SIGN_MESSAGE_PARA));
  167 
  168       SigParams.cbSize = sizeof(CRYPT_SIGN_MESSAGE_PARA);
  169       SigParams.dwMsgEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING;
  170       SigParams.pSigningCert = pCertContext;
  171       SigParams.HashAlgorithm.pszObjId = szOID_RSA_SHA1RSA;
  172       SigParams.HashAlgorithm.Parameters.pbData = NULL;
  173       SigParams.HashAlgorithm.Parameters.cbData = 0;
  174       SigParams.pvHashAuxInfo = NULL;
  175       SigParams.cMsgCert = 1;
  176       SigParams.rgpMsgCert = &pCertContext;
  177       SigParams.cMsgCrl = 0;
  178       SigParams.rgpMsgCrl = NULL;
  179       SigParams.cAuthAttr = 0;
  180       SigParams.rgAuthAttr = NULL;
  181       SigParams.cUnauthAttr = 0;
  182       SigParams.rgUnauthAttr = NULL;
  183       SigParams.dwFlags = 0;
  184       SigParams.dwInnerContentType = 0;
  185 
  186       BYTE *bsignature = (BYTE *) signature;
  187       DWORD bsignature_len = (DWORD) *signature_len;
  188 
  189       if (!CryptSignMessage(
  190             &SigParams,            // Signature parameters
  191             TRUE,                  // detached ?
  192             1,                     // Number of messages
  193             MessageArray,          // Messages to be signed
  194             MessageSizeArray,      // Size of messages
  195             bsignature,            // Buffer for signed message
  196             &bsignature_len)) {
  197          CertFreeCertificateContext(pCertContext);
  198          CertCloseStore(hStore, 0);
  199          *signature_len = bsignature_len;
  200 
  201          throw TLitePDFException(GetLastError(), "Failed to sign data");
  202       }
  203 
  204       *signature_len = bsignature_len;
  205       CertFreeCertificateContext(pCertContext);
  206       CertCloseStore(hStore, 0);
  207    }
  208 
  209    unsigned int CreateSignatureField(litePDF::TLitePDF &litePDF,
  210                                      const char *signatureName,
  211                                      __int64 dateOfSign,
  212                                      unsigned int annotationResourceID,
  213                                      unsigned int annotationPageIndex,
  214                                      RECT annotationPosition_mm,
  215                                      unsigned int annotationFlags,
  216                                      int signatureLen)
  217    {
  218       unsigned int signatureIndex;
  219 
  220       litePDF.SetSignatureSize(signatureLen);
  221 
  222       signatureIndex = litePDF.CreateSignature(signatureName,
  223                                                annotationPageIndex,
  224                                                annotationPosition_mm.left,
  225                                                annotationPosition_mm.top,
  226                                                annotationPosition_mm.right - annotationPosition_mm.left,
  227                                                annotationPosition_mm.bottom - annotationPosition_mm.top,
  228                                                annotationFlags);
  229 
  230       litePDF.SetSignatureReason(signatureIndex, L"litePDF example");
  231       litePDF.SetSignatureDate(signatureIndex, dateOfSign);
  232 
  233       if (annotationResourceID && annotationPageIndex < litePDF.GetPageCount() ) {
  234          litePDF.SetSignatureAppearance(signatureIndex, LitePDFAppearance_Normal, annotationResourceID, 0, 0);
  235       }
  236 
  237       return signatureIndex;
  238    }
  239 
  240  public:
  241    MPdfSigner()
  242    {
  243       bytes = NULL;
  244       bytes_len = 0;
  245       lastSignatureIndex = ~0;
  246    }
  247 
  248    ~MPdfSigner()
  249    {
  250       Clear();
  251    }
  252 
  253    void Clear(void)
  254    {
  255       if (bytes) {
  256          free(bytes);
  257          bytes = NULL;
  258       }
  259       bytes_len = 0;
  260       lastSignatureIndex = ~0;
  261    }
  262 
  263    void SignToFile(litePDF::TLitePDF &litePDF,
  264                    const char *fileName,
  265                    const char *signatureName)
  266    {
  267       RECT rect_mm = {0, 0, 0, 0};
  268 
  269       SignToFileEx(litePDF, fileName, signatureName, 0, 0, 0, rect_mm, 0, 0);
  270    }
  271 
  272    void SignToFileEx(litePDF::TLitePDF &litePDF,
  273                      const char *fileName,
  274                      const char *signatureName,
  275                      __int64 dateOfSign,
  276                      unsigned int annotationResourceID,
  277                      unsigned int annotationPageIndex,
  278                      RECT annotationPosition_mm,
  279                      unsigned int annotationFlags,
  280                      int signatureLen)
  281    {
  282       unsigned int signatureIndex;
  283 
  284       signatureIndex = CreateSignatureField(litePDF,
  285                                             signatureName,
  286                                             dateOfSign,
  287                                             annotationResourceID,
  288                                             annotationPageIndex,
  289                                             annotationPosition_mm,
  290                                             annotationFlags,
  291                                             signatureLen);
  292 
  293       litePDF.SaveToFileWithSignManual(fileName,
  294                                        signatureIndex,
  295                                        appendSignatureData, this,
  296                                        finishSignature, this);
  297    }
  298 
  299    bool SignToData(litePDF::TLitePDF &litePDF,
  300                    BYTE *data,
  301                    unsigned int *dataLength,
  302                    const char *signatureName)
  303    {
  304       RECT rect_mm = {0, 0, 0, 0};
  305 
  306       return SignToDataEx(litePDF, data, dataLength,
  307                           signatureName,
  308                           0, 0, 0, rect_mm, 0, 0);
  309    }
  310 
  311    bool SignToDataEx(litePDF::TLitePDF &litePDF,
  312                      BYTE *data,
  313                      unsigned int *dataLength,
  314                      const char *signatureName,
  315                      __int64 dateOfSign,
  316                      unsigned int annotationResourceID,
  317                      unsigned int annotationPageIndex,
  318                      RECT annotationPosition_mm,
  319                      unsigned int annotationFlags,
  320                      int signatureLen)
  321    {
  322       unsigned int signatureIndex = lastSignatureIndex;
  323 
  324       if (signatureIndex == (unsigned int) ~0) {
  325          signatureIndex = CreateSignatureField(litePDF,
  326                                                signatureName,
  327                                                dateOfSign,
  328                                                annotationResourceID,
  329                                                annotationPageIndex,
  330                                                annotationPosition_mm,
  331                                                annotationFlags,
  332                                                signatureLen);
  333 
  334          // remember the used signature index for the second call,
  335          // when populating the 'data'
  336          lastSignatureIndex = signatureIndex;
  337       } else {
  338          signatureIndex = lastSignatureIndex;
  339       }
  340 
  341       return litePDF.SaveToDataWithSignManual(signatureIndex,
  342                                               appendSignatureData, this,
  343                                               finishSignature, this,
  344                                               data, dataLength);
  345    }
  346 };
  347 
  348 static void drawText(HDC hDC,
  349                      const char *msg,
  350                      int px,
  351                      int py,
  352                      int fontHeight)
  353 {
  354    LOGFONTA lf = {0, };
  355    lf.lfHeight = fontHeight;
  356    strcpy(lf.lfFaceName, "Helvetica");
  357 
  358    HFONT fnt;
  359    HGDIOBJ oldFnt;
  360 
  361    fnt = CreateFontIndirect(&lf);
  362    oldFnt = SelectObject(hDC, fnt);
  363 
  364    SetTextColor(hDC, RGB(0, 0, 0));
  365    TextOut(hDC, px, py, msg, strlen(msg));
  366 
  367    SelectObject(hDC, oldFnt);
  368    DeleteObject(fnt);
  369 }
  370 
  371 static void addPage(litePDF::TLitePDF &litePDF,
  372                     unsigned int pageWidth,
  373                     unsigned int pageHeight,
  374                     const char *msg)
  375 {
  376    HDC hDC;
  377 
  378    // add a new page, with large-enough pixel scale
  379    hDC = litePDF.AddPage(litePDF.MMToUnit(pageWidth), litePDF.MMToUnit(pageHeight),
  380                          pageWidth * 10, pageHeight * 10,
  381                          LitePDFDrawFlag_SubstituteFonts);
  382 
  383    // draw the text, with ~5mm font height
  384    drawText(hDC, msg, 100, 100, -50);
  385 
  386    // finish drawing
  387    litePDF.FinishPage(hDC);
  388 }
  389 
  390 static unsigned int createResource(litePDF::TLitePDF &litePDF, const char *secondLine)
  391 {
  392    HDC hDC;
  393    int w = 25, h = 8;
  394 
  395    // create a new resource
  396    hDC = litePDF.AddResource(litePDF.MMToUnit(w), litePDF.MMToUnit(h), w * 20, h * 20, LitePDFDrawFlag_SubstituteFonts);
  397 
  398    LOGBRUSH brush;
  399    brush.lbColor = RGB(128,128,128);
  400    brush.lbHatch = 0;
  401    brush.lbStyle = BS_SOLID;
  402 
  403    HPEN pen = ExtCreatePen(PS_GEOMETRIC | PS_DOT | PS_ENDCAP_FLAT | PS_JOIN_BEVEL, 10, &brush, 0, NULL);
  404    HGDIOBJ prevPen = SelectObject(hDC, pen);
  405 
  406    // rectangle on boundaries
  407    MoveToEx(hDC, 0, 0, NULL);
  408    LineTo(hDC, w * 20, 0);
  409    LineTo(hDC, w * 20, h * 20);
  410    LineTo(hDC, 0, h * 20);
  411    LineTo(hDC, 0, 0);
  412 
  413    SelectObject(hDC, prevPen);
  414    DeleteObject(pen);
  415 
  416    // draw the text
  417    drawText(hDC, "litePDF example", 50, 20, -50);
  418    drawText(hDC, secondLine, 50, 70, -50);
  419 
  420    // finish drawing
  421    return litePDF.FinishResource(hDC);
  422 }
  423 
  424 static BYTE *getFileAsData(const char *fileName, unsigned int *dataLength)
  425 {
  426    FILE *f = fopen(fileName, "rb");
  427    if (!f) {
  428       std::string msg = "Failed to open " + std::string(fileName);
  429       throw TLitePDFException(ERROR_CANNOT_MAKE, msg.c_str());
  430    }
  431 
  432    if (fseek(f, 0, SEEK_END) != 0) {
  433       fclose(f);
  434       throw TLitePDFException(ERROR_CANNOT_MAKE,
  435                               "Failed to move to the end of the file");
  436    }
  437 
  438    *dataLength = ftell(f);
  439 
  440    if (fseek(f, 0, SEEK_SET) != 0) {
  441       fclose(f);
  442       throw TLitePDFException(ERROR_CANNOT_MAKE,
  443                               "Failed to move to the beginning of the file");
  444    }
  445 
  446    BYTE *data = (BYTE *) malloc(sizeof(BYTE) * (*dataLength));
  447    if (!data) {
  448       fclose(f);
  449       throw TLitePDFException(ERROR_OUTOFMEMORY, "Out of memory");
  450    }
  451 
  452    if (fread(data, sizeof(BYTE), *dataLength, f) != *dataLength) {
  453       fclose(f);
  454       free(data);
  455       throw TLitePDFException(ERROR_CANNOT_MAKE, "Failed to read whole file");
  456    }
  457 
  458    fclose(f);
  459    return data;
  460 }
  461 
  462 int main(void)
  463 {
  464    int res = 0;
  465    BYTE *data = NULL, *oldSignature = NULL, *newSignature = NULL;
  466    unsigned int oldSignatureLen = 0, newSignatureLen = 0;
  467 
  468    using namespace litePDF;
  469 
  470    try {
  471       MPdfSigner signer;
  472       TLitePDF litePDF;
  473 
  474       // create a document
  475       litePDF.CreateMemDocument();
  476 
  477       // create some pages
  478       addPage(litePDF, 297, 210, "Digitally signed document");
  479       addPage(litePDF, 297, 210, "Page 2");
  480 
  481       // save signed to file
  482       signer.Clear();
  483       signer.SignToFile(litePDF, "sign-1.pdf", "Sig1");
  484 
  485       // close the document
  486       litePDF.Close();
  487 
  488       //-----------------------------------------------------------------
  489 
  490       // create a document
  491       litePDF.CreateMemDocument();
  492 
  493       // create some pages
  494       addPage(litePDF, 297, 210, "Digitally signed document (2)");
  495       addPage(litePDF, 297, 210, "Page 2");
  496 
  497       // prepare the signer (clear data from the previous run)
  498       signer.Clear();
  499 
  500       // sign to data
  501       unsigned int dataLength = 0;
  502       if (!signer.SignToData(litePDF, NULL, &dataLength, "Sig2")) {
  503          std::string msg = "Failed to sign document to data (pass 1): " +
  504                            std::string(litePDF.getLastErrorMessage());
  505          throw TLitePDFException(ERROR_INVALID_DATA, msg.c_str());
  506       }
  507 
  508       data = (BYTE *) malloc(sizeof(BYTE) * dataLength);
  509       if (!data) {
  510          std::string msg = "Failed to allocate " + to_string(dataLength) + " bytes";
  511          throw TLitePDFException(ERROR_OUTOFMEMORY, msg.c_str());
  512       }
  513 
  514       if (!signer.SignToData(litePDF, data, &dataLength, "Sig2")) {
  515          std::string msg = "Failed to sign document to data (pass 2): " +
  516                            std::string(litePDF.getLastErrorMessage());
  517          throw TLitePDFException(ERROR_INVALID_DATA, msg.c_str());
  518       }
  519 
  520       // write data to file
  521       FILE *f = fopen("sign-2.pdf", "wb");
  522       if (!f) {
  523          throw TLitePDFException(ERROR_INVALID_DATA, "Failed to open 'sign-2.pdf'");
  524       }
  525 
  526       if (fwrite(data, sizeof(BYTE), dataLength, f) != dataLength) {
  527          fclose(f);
  528          throw TLitePDFException(ERROR_INVALID_DATA,
  529                                  "Failed to write all data to 'sign-2.pdf'");
  530       }
  531 
  532       fclose(f);
  533 
  534       // close the document
  535       litePDF.Close();
  536 
  537       //-----------------------------------------------------------------
  538 
  539       // create a document and save it with visual appearance of a signature
  540       litePDF.CreateMemDocument();
  541 
  542       // create some pages
  543       addPage(litePDF, 297, 210, "Digitally signed document (incremental)");
  544       addPage(litePDF, 297, 210, "Page 2");
  545 
  546       // create also visual appearance for this signature, on the first page
  547       RECT where_mm = { litePDF.MMToUnit(90),      litePDF.MMToUnit(5),
  548                         litePDF.MMToUnit(90 + 25), litePDF.MMToUnit(5 + 8) };
  549 
  550       time_t dataOfSign;
  551 
  552       time(&dataOfSign);
  553       // two days ago
  554       dataOfSign -= 2 * 24 * 60 * 60;
  555 
  556       // sign to a new file
  557       signer.Clear();
  558       signer.SignToFileEx(litePDF, "sign-3.pdf", "Sig3", dataOfSign,
  559          createResource(litePDF, "1st Signature"),
  560          0,
  561          where_mm,
  562          LitePDFAnnotationFlag_None,
  563          1280);
  564 
  565       // close the document
  566       litePDF.Close();
  567 
  568       //-----------------------------------------------------------------
  569 
  570       // copy the file for testing
  571       if (!CopyFile("sign-3.pdf", "sign-4.pdf", FALSE)) {
  572          throw TLitePDFException(GetLastError(),
  573                                  "Failed to copy sign-3.pdf to sign-4.pdf");
  574       }
  575 
  576       //-----------------------------------------------------------------
  577 
  578       // remember to load for update to add signatures to already signed documents;
  579       // (uses 'loadCompletely=true' to be able to overwrite the file)
  580       // Note that this invalidates 'Sig3' signature when used without the authorization
  581       // key, due to the modification of the first page content.
  582       litePDF.LoadFromFile("sign-4.pdf", NULL, true, true);
  583 
  584       // check whether is already signed
  585       if (!litePDF.GetDocumentIsSigned()) {
  586          throw TLitePDFException(ERROR_INVALID_DATA,
  587                "Expected the opened file already signed, but it is not");
  588       }
  589 
  590       // there should be only one signature
  591       if (litePDF.GetSignatureCount() != 1) {
  592          std::string str;
  593          str = "Expected the opened file has one signature, but it has " +
  594             to_string(litePDF.GetSignatureCount()) + " signatures";
  595          throw TLitePDFException(ERROR_INVALID_DATA, str.c_str());
  596       }
  597 
  598       // get the first (current) signature
  599       if (!litePDF.GetSignatureData(0, NULL, &oldSignatureLen)) {
  600          throw TLitePDFException(ERROR_INVALID_DATA,
  601             "Failed to get the first signature length");
  602       }
  603 
  604       if (!oldSignatureLen) {
  605          throw TLitePDFException(ERROR_INVALID_DATA,
  606             "Failed to get the first signature length value");
  607       }
  608 
  609       if (oldSignatureLen != 1280) {
  610          std::string str;
  611          str = "Expected 1280 bytes long signature, but reported is " +
  612             to_string(oldSignatureLen) + " bytes";
  613          throw TLitePDFException(ERROR_INVALID_DATA, str.c_str());
  614       }
  615 
  616       oldSignature = (BYTE *) malloc(sizeof(BYTE) * oldSignatureLen);
  617       if (!oldSignature) {
  618          std::string msg = "Failed to allocate " + to_string(oldSignatureLen) + " bytes";
  619          throw TLitePDFException(ERROR_OUTOFMEMORY, msg.c_str());
  620       }
  621 
  622       if (!litePDF.GetSignatureData(0, oldSignature, &oldSignatureLen)) {
  623          throw TLitePDFException(ERROR_INVALID_DATA, "Failed to get the first signature data");
  624       }
  625 
  626       unsigned int ii;
  627       printf ("Signature[0] of sign-4.pdf is %d bytes long:", oldSignatureLen);
  628       for (ii = 0; ii < oldSignatureLen; ii++) {
  629          if ((ii % 16) == 0) {
  630             printf ("\n   ");
  631          } else {
  632             printf (" ");
  633             if ((ii % 8) == 0) {
  634                printf ("  ");
  635             }
  636          }
  637 
  638          printf ("%02x", oldSignature[ii]);
  639       }
  640       printf ("\n");
  641 
  642       // add another signature
  643       signer.Clear();
  644       signer.SignToFile(litePDF, "sign-4.pdf", "Sig4");
  645 
  646       // close the document
  647       litePDF.Close();
  648 
  649       //-----------------------------------------------------------------
  650 
  651       if (data) {
  652          free(data);
  653       }
  654 
  655       data = getFileAsData("sign-4.pdf", &dataLength);
  656 
  657       //-----------------------------------------------------------------
  658 
  659       // add yet another signature and compare the first signature that
  660       // it did not change
  661 
  662       litePDF.LoadFromData(data, dataLength, NULL, true);
  663 
  664       // check whether is already signed
  665       if (!litePDF.GetDocumentIsSigned()) {
  666          throw TLitePDFException(ERROR_INVALID_DATA,
  667                "Expected the opened file already signed, but it is not");
  668       }
  669 
  670       // there should be only one signature
  671       if (litePDF.GetSignatureCount() != 2) {
  672          std::string str;
  673          str = "Expected the opened file has two signatures, but it has " +
  674             to_string(litePDF.GetSignatureCount()) + " signatures";
  675          throw TLitePDFException(ERROR_INVALID_DATA, str.c_str());
  676       }
  677 
  678       for (ii = 0; ii < 2; ii++) {
  679          free(data);
  680          data = NULL;
  681 
  682          if (newSignature) {
  683             free(newSignature);
  684             newSignature = NULL;
  685          }
  686 
  687          newSignatureLen = 0;
  688 
  689          // get the signature data
  690          if (!litePDF.GetSignatureData(ii, NULL, &newSignatureLen)) {
  691             std::string msg = "Failed to get signature[" + to_string(ii) + "] length";
  692             throw TLitePDFException(ERROR_INVALID_DATA, msg.c_str());
  693          }
  694 
  695          if (!newSignatureLen) {
  696             std::string msg = "Failed to get signature[" + to_string(ii) + "] length value";
  697             throw TLitePDFException(ERROR_INVALID_DATA, msg.c_str());
  698          }
  699 
  700          newSignature = (BYTE *) malloc(sizeof(BYTE) * newSignatureLen);
  701          if (!newSignature) {
  702             std::string msg = "Failed to allocate " + to_string(newSignatureLen) + " bytes";
  703             throw TLitePDFException(ERROR_OUTOFMEMORY, msg.c_str());
  704          }
  705 
  706          if (!litePDF.GetSignatureData(ii, newSignature, &newSignatureLen)) {
  707             std::string msg = "Failed to get signature[" + to_string(ii) + "] data";
  708             throw TLitePDFException(ERROR_INVALID_DATA, msg.c_str());
  709          }
  710 
  711          if (ii == 0) {
  712             if (newSignatureLen != oldSignatureLen ||
  713                 memcmp(oldSignature, newSignature, newSignatureLen) != 0) {
  714                throw TLitePDFException(ERROR_INVALID_DATA, "The first signature may not change");
  715             }
  716          } else {
  717             if (newSignatureLen == oldSignatureLen &&
  718                 memcmp(oldSignature, newSignature, newSignatureLen) == 0) {
  719                throw TLitePDFException(ERROR_INVALID_DATA, "The first and the second signature may not match");
  720             }
  721          }
  722       }
  723 
  724       if (data) {
  725          free(data);
  726          data = NULL;
  727       }
  728       dataLength = 0;
  729 
  730       // add another signature, but sign to data this time
  731       signer.Clear();
  732 
  733       dataLength = 0;
  734       if (!signer.SignToDataEx(litePDF, NULL, &dataLength, "Sig5", 0,
  735             createResource(litePDF, "2nd Signature"), 1, where_mm, 0, 0)) {
  736          std::string msg = "Failed to IncSign document to data (pass 1): " +
  737                            std::string(litePDF.getLastErrorMessage());
  738          throw TLitePDFException(ERROR_INVALID_DATA, msg.c_str());
  739       }
  740 
  741       data = (BYTE *) malloc(sizeof(BYTE) * dataLength);
  742       if (!data) {
  743          std::string msg = "Failed to allocate " + to_string(dataLength) + " bytes";
  744          throw TLitePDFException(ERROR_OUTOFMEMORY, msg.c_str());
  745       }
  746 
  747       // no need to call SignToDataEx again, this receives only the data
  748       if (!signer.SignToData(litePDF, data, &dataLength, "Sig5")) {
  749          std::string msg = "Failed to IncSign document to data (pass 2): " +
  750                            std::string(litePDF.getLastErrorMessage());
  751          throw TLitePDFException(ERROR_INVALID_DATA, msg.c_str());
  752       }
  753 
  754       if (!data || !dataLength) {
  755          throw TLitePDFException(ERROR_INVALID_DATA, "Failed to IncSign to data");
  756       }
  757 
  758       // close the document
  759       litePDF.Close();
  760 
  761       //-----------------------------------------------------------------
  762 
  763       f = fopen("sign-5.pdf", "wb");
  764       if (!f) {
  765          throw TLitePDFException(ERROR_INVALID_DATA, "Failed to open sign-5.pdf for writing");
  766       }
  767 
  768       if (fwrite(data, sizeof(BYTE), dataLength, f) != dataLength) {
  769          fclose(f);
  770          throw TLitePDFException(ERROR_INVALID_DATA, "Failed to write data to sign-5.pdf");
  771       }
  772 
  773       fclose(f);
  774 
  775       //-----------------------------------------------------------------
  776    } catch (TLitePDFException &ex) {
  777       fprintf(stderr, "litePDF Exception: %x: %s\n", ex.getCode(), ex.getMessage());
  778       res = 1;
  779    }
  780 
  781    if (data) {
  782       free(data);
  783    }
  784 
  785    if (oldSignature) {
  786       free(oldSignature);
  787    }
  788 
  789    if (newSignature) {
  790       free(newSignature);
  791    }
  792 
  793    return res;
  794 }
TOPlist