Individual Module

Introduction

Sometimes it is needed to delegate the signature process to e.g. an external webservice. This is possible by implementing your own signature module.

You can extend an existing module or implement your own module class by implementing the \setasign\SetaPDF2\Signer\Signature\Module\ModuleInterface interface.

Additional interfaces are available which allow modifications of the signature dictionary or the document instance: \setasign\SetaPDF2\Signer\Signature\Module\DictionaryInterface and \setasign\SetaPDF2\Signer\Signature\Module\DocumentInterface.

Examples

External Serivce that Creates CMS/PKCS#7 Structures

Following example delegates the complete creation of the CMS/PKCS#7 structure to an external service:

PHP
<?php

use setasign\SetaPDF2\Core\Document;
use setasign\SetaPDF2\Core\Reader\FilePath;
use setasign\SetaPDF2\Core\Type\PdfDictionary;
use setasign\SetaPDF2\Core\Type\PdfName;
use setasign\SetaPDF2\Signer\Signature\Module\DictionaryInterface;
use setasign\SetaPDF2\Signer\Signature\Module\DocumentInterface;
use setasign\SetaPDF2\Signer\Signature\Module\ModuleInterface;

class MySignatureModule implements
    ModuleInterface,
    DictionaryInterface,
    DocumentInterface
{
    /**
     * Create a signature for the file in the given $tmpPath.
     *
     * @param FilePath $tmpPath
     * @return string
     */
    public function createSignature(FilePath $tmpPath)
    {
        // get hash
        $hashAlgorithm = 'sha256';
        $hashValue = hash_file($hashAlgorithm, $tmpPath);

        // e.g. call an external webservice to create the signature
        $signature = callWebserviceToCreateSignature($hashValue, $hashAlgorithm);

        // return the signature value
        return $signature;
    }

    /**
     * Method to update the signature dictionary.
     *
     * @param PdfDictionary $dictionary
     */
    public function updateSignatureDictionary(PdfDictionary $dictionary)
    {
        // update the signature dictionary if needed

        // if the webservice returns a PAdES conforming signature update following values:
        //$dictionary['SubFilter'] = new PdfName('ETSI.CAdES.detached', true);
        //$dictionary['Filter'] = new PdfName('Adobe.PPKLite', true);
    }

    /**
     * Method to allow updates onto the document instance.
     *
     * @param Document $document
     */
    public function updateDocument(Document $document)
    {
        // make changes or check to the document instance

        // if the webservice returns a PAdES conforming signature update following values:
        //$extensions = $document->getCatalog()->getExtensions();
        //$extensions->setExtension('ESIC', '1.7', 2);
    }
}

Exernal Service that Returns a PKCS#1 Signature Value

If a service returns only a signature value (e.g. PKCS#1) it's needed to create the final CMS structure and embed the signature value into it. This is supported by the \setasign\SetaPDF2\Signer\Signature\Module\Cms and \setasign\SetaPDF2\Signer\Signature\Module\Pades modules which both come with a setSignatureValue() method.

As the PAdES module is the most common used module we prepared a trait which proxies most methods of it. By using this, a simple module that embeds a returned signature value into the CMS structure could look like this:

PHP
<?php

use setasign\SetaPDF2\Core\Reader\FilePath;
use setasign\SetaPDF2\Signer\Signature\Module\DictionaryInterface;
use setasign\SetaPDF2\Signer\Signature\Module\DocumentInterface;
use setasign\SetaPDF2\Signer\Signature\Module\ModuleInterface;
use setasign\SetaPDF2\Signer\Signature\Module\PadesProxyTrait;

class MySignatureModule implements
    ModuleInterface,
    DictionaryInterface,
    DocumentInterface
{
    use PadesProxyTrait;

    /**
     * Create a signature for the file in the given $tmpPath.
     *
     * @param FilePath $tmpPath
     * @return string
     */
    public function createSignature(FilePath $tmpPath)
    {
        $hashAlgorithm = 'sha256'; // let's fixiate this for this example
        $padesModule = $this->_getPadesModule();
        $padesModule->setDigest($hashAlgorithm);

        $hashValue = hash($hashAlgorithm, $padesModule->getDataToSign($tmpPath), true);
        // e.g. call an external webservice to create the signature value
        $signatureValue = callWebserviceToCreateSignature($hashValue, $hashAlgorithm);

        // pass the signature value to the CMS structure
        $padesModule->setSignatureValue($signatureValue);

        return $padesModule->getCms();
    }
}

Build Properties Dictionary

If you want embed information about the software or module that was involved in the signing process, you can save these information in the Build Properties Dictionary, which you can create at runtime by implementing the \setasign\SetaPDF2\Signer\Signature\Module\DictionaryInterface. For details about the content of this Build Properties Dictionary please see here.

To display the application name and version in e.g. Adobe Acrobat such as this:

You can implement this in your own module like this:

PHP
<?php

use setasign\SetaPDF2\Core\Reader\FilePath;
use setasign\SetaPDF2\Core\Type\PdfDictionary;
use setasign\SetaPDF2\Core\Type\PdfName;
use setasign\SetaPDF2\Core\Type\PdfString;
use setasign\SetaPDF2\Signer\Signature\Module\DictionaryInterface;
use setasign\SetaPDF2\Signer\Signature\Module\DocumentInterface;
use setasign\SetaPDF2\Signer\Signature\Module\ModuleInterface;
use setasign\SetaPDF2\Signer\Signature\Module\PadesProxyTrait;

class MySignatureModule implements
    ModuleInterface,
    DictionaryInterface,
    DocumentInterface
{
    use PadesProxyTrait {
        PadesProxyTrait::updateSignatureDictionary as updateSignatureDictionaryWithPadesInformation;
    }

    public function updateSignatureDictionary(PdfDictionary $dictionary)
    {
        $this->updateSignatureDictionaryWithPadesInformation($dictionary);

        $dictionary['Prop_Build'] = new PdfDictionary([
            'App' => new PdfDictionary([
                'REx' => new PdfString('1.2.3'),
                'Name' => new PdfName('My awesome signature service')
            ])
        ]);
    }

    public function createSignature(FilePath $tmpPath)
    {
        // ...
    }
}