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 SetaPDF_Signer_Signature_Module_ModuleInterface interface.

Additional interfaces are available which allow modifications of the signature dictionary or the document instance: SetaPDF_Signer_Signature_DictionaryInterface and SetaPDF_Signer_Signature_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
class MySignatureModule implements 
    \SetaPDF_Signer_Signature_Module_ModuleInterface,
    \SetaPDF_Signer_Signature_DictionaryInterface,
    \SetaPDF_Signer_Signature_DocumentInterface
{
    /**
     * Create a signature for the file in the given $tmpPath.
     *
     * @param \SetaPDF_Core_Reader_FilePath $tmpPath
     * @return string
     */
    public function createSignature(\SetaPDF_Core_Reader_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 \SetaPDF_Core_Type_Dictionary $dictionary
     */
    public function updateSignatureDictionary(\SetaPDF_Core_Type_Dictionary $dictionary)
    {
        // update the signature dictionary if needed
        
        // if the webservice returns a PAdES conforming signature update following values:
        //$dictionary['SubFilter'] = new \SetaPDF_Core_Type_Name('ETSI.CAdES.detached', true);
        //$dictionary['Filter'] = new \SetaPDF_Core_Type_Name('Adobe.PPKLite', true);
    }

    /**
     * Method to allow updates onto the document instance.
     *
     * @param SetaPDF_Core_Document $document
     */
    public function updateDocument(\SetaPDF_Core_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 SetaPDF_Signer_Signature_Module_Cms and SetaPDF_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
class MySignatureModule implements 
    \SetaPDF_Signer_Signature_Module_ModuleInterface,
    \SetaPDF_Signer_Signature_DictionaryInterface,
    \SetaPDF_Signer_Signature_DocumentInterface
{
    use \SetaPDF_Signer_Signature_Module_PadesProxyTrait;
    
    /**
     * Create a signature for the file in the given $tmpPath.
     *
     * @param SetaPDF_Core_Reader_FilePath $tmpPath
     * @return string
     */
    public function createSignature(\SetaPDF_Core_Reader_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 SetaPDF_Signer_Signature_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

class MySignatureModule implements 
    \SetaPDF_Signer_Signature_Module_ModuleInterface,
    \SetaPDF_Signer_Signature_DictionaryInterface,
    \SetaPDF_Signer_Signature_DocumentInterface
{
    use \SetaPDF_Signer_Signature_Module_PadesProxyTrait {
        \SetaPDF_Signer_Signature_Module_PadesProxyTrait::updateSignatureDictionary as updateSignatureDictionaryWithPadesInformation;
    }
    
    public function updateSignatureDictionary(\SetaPDF_Core_Type_Dictionary $dictionary)
    {
        $this->updateSignatureDictionaryWithPadesInformation($dictionary);

        $dictionary['Prop_Build'] = new \SetaPDF_Core_Type_Dictionary([
            'App' => new \SetaPDF_Core_Type_Dictionary([
                'REx' => new \SetaPDF_Core_Type_String('1.2.3'),
                'Name' => new \SetaPDF_Core_Type_Name('My awesome signature service')
            ])
        ]);
    }
}