cpdfpodofo

Insert multiple(2 times)digital signatures,i found there are 3 info-dictionary in the pdf


i got the same problem( develop with podofo in c++). after Insert multiple(2 times)digital signatures,i found there are 3 info——dictionary in the pdf file:

how to add two digital signature without invalidating the previous one?

thanks!

i open file in notepad++,and i found the different

the first:  97 0 obj<</Title(? G I S e r C l u bThR\n 2 0 1 4 0 7 2 0) /Author(edison qian) /Keywords(GISerClub) /Creator(? M i c r o s o f t ?  W o r d   2 0 1 3) 
            /CreationDate(D:20150601200942+08'00') /ModDate(D:20150601200942+08'00') /Producer(? M i c r o s o f t ?  W o r d   2 0 1 3) >>

the second: 97 0 obj<</Author(edison qian)/CreationDate(D:20150601200942+08'00')/Creator(? M i c r o s o f t ?  W o r d   2 0 1 3)/Keywords(GISerClub)
            /ModDate(D:20190426155330+08'00')/Producer(? M i c r o s o f t ?  W o r d   2 0 1 3)/Title(? G I S e r C l u bThR\n 2 0 1 4 0 7 2 0)>>

the third:  97 0 obj<</Author(edison qian)/CreationDate(D:20150601200942+08'00')/Creator(? M i c r o s o f t ?  W o r d   2 0 1 3)/Keywords(GISerClub)
            /ModDate(D:20190426155428+08'00')/Producer(? M i c r o s o f t ?  W o r d   2 0 1 3)/Title(? G I S e r C l u bThR\n 2 0 1 4 0 7 2 0)>>

my code:



    bool pdfSign(PdfMemDocument* document,PdfOutputDevice* outputDevice,PKCS12* p12,RSA* rsa,int npage,PdfRect rect,int min_signature_size,const char* ImgFile/*,PdfDate& sigData*/)
    {
        PdfInfo* pInfo = document->GetInfo();
        TKeyMap itm = pInfo->GetObject()->GetDictionary().GetKeys();
        PdfObject* pobj = pInfo->GetObject()->GetDictionary().GetKey(PdfName("ModDate"));
        PdfString modDate = pobj->GetString();
        string sDate = modDate.GetString();
        string sutf8Date = modDate.GetStringUtf8();

        PdfOutlines* pOutLine = document->GetOutlines();
        TKeyMap itm2 = pOutLine->GetObject()->GetDictionary().GetKeys();

        const char *field_name = NULL;
        bool field_use_existing = false;
        int annot_page = npage;
        //double annot_left = 80.0, annot_top = 70.0, annot_width = 150.0, annot_height = 150.0;
        bool annot_print = true;
        const char *reason = "I agree";

        int result = 0;
        PdfSignatureField *pSignField = NULL;

        try
        {
            PdfSignOutputDevice signer( outputDevice );

            PdfAcroForm* pAcroForm = document->GetAcroForm();
            if( !pAcroForm )
                PODOFO_RAISE_ERROR_INFO( ePdfError_InvalidHandle, "acroForm == NULL" );

            if( !pAcroForm->GetObject()->GetDictionary().HasKey( PdfName( "SigFlags" ) ) || 
                !pAcroForm->GetObject()->GetDictionary().GetKey( PdfName( "SigFlags" ) )->IsNumber() || 
                pAcroForm->GetObject()->GetDictionary().GetKeyAsLong( PdfName( "SigFlags" ) ) != 3 )
            {
                if( pAcroForm->GetObject()->GetDictionary().HasKey( PdfName( "SigFlags" ) ) )
                    pAcroForm->GetObject()->GetDictionary().RemoveKey( PdfName( "SigFlags" ) );

                pdf_int64 val = 3;
                pAcroForm->GetObject()->GetDictionary().AddKey( PdfName( "SigFlags" ), PdfObject( val ) );
            }

            if( pAcroForm->GetNeedAppearances() )
            {
                #if 0 /* TODO */
                update_default_appearance_streams( pAcroForm );
                #endif

                pAcroForm->SetNeedAppearances( false );
            }

            PdfString name;
            PdfObject* pExistingSigField = NULL;

            PdfImage image( document );
            image.LoadFromFile( ImgFile );
            double dimgWidth = image.GetWidth();
            double dimgHeight = image.GetHeight();

            char fldName[96]; // use bigger buffer to make sure sprintf does not overflow
            sprintf( fldName, "PodofoSignatureField%" PDF_FORMAT_INT64, static_cast( document->GetObjects().GetObjectCount() ) );
            name = PdfString( fldName );

            PdfPage* pPage = document->GetPage( annot_page );
            if( !pPage )
                PODOFO_RAISE_ERROR( ePdfError_PageNotFound );

            double dPageHeight = pPage->GetPageSize().GetHeight();
            double dPageWidth = pPage->GetPageSize().GetWidth();

            PdfRect annot_rect;
            annot_rect = PdfRect( rect.GetLeft(), 
                pPage->GetPageSize().GetHeight() - rect.GetBottom() - rect.GetHeight(),
                dimgWidth, 
                dimgHeight );

            PdfAnnotation* pAnnot = pPage->CreateAnnotation( ePdfAnnotation_Widget, annot_rect );
            if( !pAnnot )
                PODOFO_RAISE_ERROR_INFO( ePdfError_OutOfMemory, "Cannot allocate annotation object" );

            if( annot_print )
                pAnnot->SetFlags( ePdfAnnotationFlags_Print );
            else if(  !field_name || !field_use_existing  )
                pAnnot->SetFlags( ePdfAnnotationFlags_Invisible | ePdfAnnotationFlags_Hidden );

            PdfPainter painter;
            try
            {
                painter.SetPage( /*&sigXObject*/pPage );

                /* Workaround Adobe's reader error 'Expected a dict object.' when the stream
                    contains only one object which does Save()/Restore() on its own, like
                    the image XObject. */
                painter.Save();
                painter.Restore();
                draw_annotation( *document, painter, image, annot_rect );

            }
            catch( PdfError & e )
            {
                if( painter.GetPage() )
                {
                    try
                    {
                        painter.FinishPage();
                    }
                    catch( ... )
                    {
                    }
                }
            }

            painter.FinishPage();

            //pSignField = new PdfSignatureField( pAnnot, pAcroForm, document );
            pSignField = new PdfSignatureField( pPage, annot_rect, document );
            if( !pSignField )
                PODOFO_RAISE_ERROR_INFO( ePdfError_OutOfMemory, "Cannot allocate signature field object" );

            PdfRect annotSize( 0.0, 0.0, dimgWidth, dimgHeight );
            PdfXObject sigXObject( annotSize, document );

            pSignField->SetAppearanceStream( &sigXObject );


            // use large-enough buffer to hold the signature with the certificate
            signer.SetSignatureSize( min_signature_size );

            pSignField->SetFieldName( name );
            pSignField->SetSignatureReason( PdfString( reinterpret_cast( reason ) ) );
            pSignField->SetSignatureDate( /*sigData*/PdfDate() );
            pSignField->SetSignature( *signer.GetSignatureBeacon() );
            pSignField->SetBackgroundColorTransparent();
            pSignField->SetBorderColorTransparent();

            // The outPdfFile != NULL means that the write happens to a new file,
            // which will be truncated first and then the content of the srcPdfFile
            // will be copied into the document, follwed by the changes.
            //signer.Seek(0);
            document->WriteUpdate( &signer, true ); 

            if( !signer.HasSignaturePosition() )
                PODOFO_RAISE_ERROR_INFO( ePdfError_SignatureError, "Cannot find signature position in the document data" );

            // Adjust ByteRange for signature
            signer.AdjustByteRange();

            // Read data for signature and count it
            // We seek at the beginning of the file
            signer.Seek( 0 );
            sign_with_signer( signer, g_x509, g_pKey );
            signer.Flush();
        }
        catch( PdfError & e )
        {

        }

        if( pSignField )
            delete pSignField;

    }

i use the code above two times, and the first signature is invalid. how to add two digital signature without invalidating the previous one?


Solution

  • Painting on the right PdfCanvas

    After analyzing the example pdf the reason why your second signature invalidated your first one became clear: In the course of signing you change the page content of the page with the widget annotation of the signature.

    But changing the content of any page invalidates any previous signature! Cf. this answer for details on allowed and disallowed changes of signed documents.

    Indeed:

    PdfPainter painter;
    
    try
    {
        painter.SetPage( /*&sigXObject*/pPage );
    
        /* Workaround Adobe's reader error 'Expected a dict object.' when the stream
            contains only one object which does Save()/Restore() on its own, like
            the image XObject. */
        painter.Save();
        painter.Restore();
        draw_annotation( *document, painter, image, annot_rect );
    }
    

    Apparently you here change something in the page content itself. When this code is executed while applying the second signature, the first signature is invalidated.

    You confirmed in a comment:

    i use '&sigXObject' instead of 'pPage ',All two signatures are working! but the red seal disappeared

    Using the right coordinates

    Concerning your observation that the red seal disappeared: You use the wrong coordinates for drawing the image on the annotation appearance!

    You use coordinates for the page coordinate system, but you have to use the coordinates in the coordinate system given by the appearance's bounding box.

    Thus, your

    painter.DrawImage( annot_rect.GetLeft(), annot_rect.GetBottom(), &image );
    

    is wrong, instead try something like

    painter.DrawImage( 0, 0, &image );
    

    as the bounding box of your appearance is is

    [ 0 0 151 151 ]