Async Signature With Timestamp
This demo shows you how to create a signature and an embedded timestamp in an async workflow.
In theory the logic in the "prepared" state can be outsourced to any service such as a client side signature solution. Another usecase for an async workflow could be a batch process.
PHP
<?php // load and register the autoload function require_once __DIR__ . '/../../../../../bootstrap.php'; // let's use a session to hold the state of the workflow session_start(); // first of all we need a path for a temporary file for the workflow $tempDir = __DIR__ . '/tmp'; if (!is_writable($tempDir)) { throw new RuntimeException('The directory for the temporary file should be writeable.'); } $state = isset($_GET['next']) && isset($_SESSION['workflow']['state']) ? $_SESSION['workflow']['state'] : null; // IN A REAL SCENARIO YOU HAVE TO MAKE SURE THAT A WORKFLOW CAN ONLY BE EXECUTED ONCE // SO YOU NEED TO LOCK THE WORKFLOW WHEN A STEP IS EXECUTED BY E.G. UPDATING THE STATE IN // THE DATABASE OR QUEUE DURING EACH STEP AND NOT ONLY AT THE END OF THE SCRIPT. if ($state === null) { $workflow = [ 'state' => null, 'id' => 1234, 'fileToSign' => $assetsDirectory . '/pdfs/camtown/Laboratory-Report.pdf', // we need a path for a temporary file which needs to be hold during the whole process // in a real world szenario you should clean up the folder depending on the state of your queue items 'tempPath' => $tempDir . '/task-id-1234.tmp', 'signature' => null ]; $document = \SetaPDF_Core_Document::loadByFilename($workflow['fileToSign']); $signer = new \SetaPDF_Signer($document); $signer->setSignatureContentLength(20000); // use an empty module instance to trigger implemented interface methods $module = new \SetaPDF_Signer_Signature_Module_Pades(); $tmpDocument = $signer->preSign(new \SetaPDF_Core_Writer_File($workflow['tempPath']), $module); $workflow['tmpDocument'] = $tmpDocument; // if you don't want to save the TmpDocument instance in your workflow, you will need at least the ByteRange // value to be able to recreate the TmpDocument instance later: // $workflow['tmpDocumentByteRange'] = $tmpDocument->getByteRange(); $workflow['state'] = 'prepared'; echo 'Document prepared. Next step: <a href="?next=' . time() . '">Create Signature</a>'; } if ($state === 'prepared') { $workflow = $_SESSION['workflow']; $document = \SetaPDF_Core_Document::loadByFilename($workflow['fileToSign']); $signer = new \SetaPDF_Signer($document); // now create a complete module instance $module = new \SetaPDF_Signer_Signature_Module_Pades(); $module->setCertificate('file://' . $assetsDirectory . '/certificates/setapdf-no-pw.pem'); $module->setPrivateKey('file://' . $assetsDirectory . '/certificates/setapdf-no-pw.pem', ''); $tmpDocument = $workflow['tmpDocument']; // If you want to create a TmpDocument instance manually, you need to do this in each step: // $tmpDocument = new \SetaPDF_Signer_TmpDocument(new \SetaPDF_Core_Writer_File($workflow['tempPath'])); // $tmpDocument->setDocumentIdentification($document); // $tmpDocument->updateIdentificationHash(); // $tmpDocument->setByteRange($workflow['tmpDocumentByteRange']); // and create the signature $workflow['signature'] = $signer->createSignature($tmpDocument, $module); // if the signature should be created by another service or application you don't need a module instance // in this step but you have to build the digest on your own. Please notice that the external service // may need the digest encoded in a specific format (e.g. hex- or base64 encoded). The following lines create // and pass a binary version of the digest. // $digestMethod = 'sha256'; // $digest = hash_file($digestMethod, $workflow['tmpDocument']->getHashFile(), true); // $workflow['signature'] = createSignatureByAnotherImplementation($digest, $digestMethod); $workflow['state'] = 'signatureCreated'; echo 'Signature created. Next step: '; echo '<a href="?next=' . time() . '×tamp=1">Add Timestamp</a> or '; echo '<a href="?next=' . time() . '">Save Signature</a> | '; echo '<a href="?' . time() . '">Restart</a>'; } if ($state === 'signatureCreated' && isset($_GET['timestamp'])) { $workflow = $_SESSION['workflow']; // catch this, if the link was clicked more than once if (isset($workflow['timestamped'])) { echo 'Signature already timestamped. Next step: '; } else { $document = \SetaPDF_Core_Document::loadByFilename($workflow['fileToSign']); $signer = new \SetaPDF_Signer($document); $url = 'https://freetsa.org/tsr'; $tsModule = new \SetaPDF_Signer_Timestamp_Module_Rfc3161_Curl($url); $tsModule->setDigest(\SetaPDF_Signer_Digest::SHA_256); $signer->setTimestampModule($tsModule); $workflow['signature'] = $signer->addTimeStamp($workflow['signature'], $workflow['tmpDocument']); $workflow['timestamped'] = true; echo 'Timestamp added. Next step: '; } echo '<a href="?next=' . time() . '">Save Signature</a> | '; echo '<a href="?' . time() . '">Restart</a>'; } elseif ($state === 'signatureCreated') { $workflow = $_SESSION['workflow']; $writer = new \SetaPDF_Core_Writer_Http('async-signature.pdf'); $document = \SetaPDF_Core_Document::loadByFilename($workflow['fileToSign'], $writer); $signer = new \SetaPDF_Signer($document); $signer->saveSignature($workflow['tmpDocument'], $workflow['signature']); } $_SESSION['workflow'] = $workflow;