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