Skip to content
Snippets Groups Projects
CertificateSignatureController.php 14.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • <?php
    
    /**
    * Copyright Maarch since 2008 under licence GPLv3.
    * See LICENCE.txt file at the root folder for more details.
    * This file is part of Maarch software.
    *
    */
    
    /**
    * @brief Certificate Signature Controller
    * @author dev@maarch.org
    */
    
    namespace Document\controllers;
    
    
    use Docserver\controllers\DocserverController;
    
    use Docserver\models\AdrModel;
    use Docserver\models\DocserverModel;
    
    use SrcCore\models\CoreConfigModel;
    
    class CertificateSignatureController
    {
    
        public static $signatureLength = 30000;
    
        public static function getHashedCertificate(array $args)
    
            session_start();
    
    
            $tmpPath = CoreConfigModel::getTmpPath();
    
            if (!empty($args['body']['tmpUniqueId'])) {
                if (is_file("{$tmpPath}tmpSignatureEsign_{$GLOBALS['id']}_{$args['body']['tmpUniqueId']}.pdf")) {
                    $pathToDocument = "{$tmpPath}tmpSignatureEsign_{$GLOBALS['id']}_{$args['body']['tmpUniqueId']}.pdf";
                }
            }
            if (empty($pathToDocument)) {
    
                $adr = AdrModel::getDocumentsAdr([
    
                    'select'  => ['path', 'filename'],
                    'where'   => ['main_document_id = ?', 'type = ?'],
                    'data'    => [$args['id'], 'ESIGN']
    
                if (!empty($adr)) {
    
                    $docserver = DocserverModel::getByType(['type' => 'ESIGN', 'select' => ['path']]);
    
                } else {
                    $adr = AdrModel::getDocumentsAdr([
                        'select' => ['path', 'filename'],
                        'where'  => ['main_document_id = ?', 'type = ?'],
                        'data'   => [$args['id'], 'DOC']
                    ]);
    
                    $docserver = DocserverModel::getByType(['type' => 'DOC', 'select' => ['path']]);
    
                $pathToDocument = $docserver['path'] . $adr[0]['path'] . $adr[0]['filename'];
    
            $signedDocumentPath = $tmpPath . $GLOBALS['id'] . '_' . rand() . '_signedDocument.pdf';
    
            $writer             = new \SetaPDF_Core_Writer_File($signedDocumentPath);
            $document           = \SetaPDF_Core_Document::loadByFilename($pathToDocument, $writer);
            $signer             = new \SetaPDF_Signer($document);
    
            $module             = new \SetaPDF_Signer_Signature_Module_Pades();
    
            $certificate        = new \SetaPDF_Signer_X509_Certificate($args['body']['certificate']);
    
    
            $module->setCertificate($certificate);
    
    
            $informationResolverManager = new \SetaPDF_Signer_InformationResolver_Manager();
    
            $informationResolverManager->addResolver(new \SetaPDF_Signer_InformationResolver_HttpCurlResolver());
            $extraCerts = new \SetaPDF_Signer_X509_Collection();
    
    
            $certificates = [$certificate];
            while (count($certificates) > 0) {
                $currentCertificate = array_pop($certificates);
    
                $aia = $currentCertificate->getExtensions()->get(\SetaPDF_Signer_X509_Extension_AuthorityInformationAccess::OID);
                if ($aia instanceof \SetaPDF_Signer_X509_Extension_AuthorityInformationAccess) {
                    foreach ($aia->fetchIssuers($informationResolverManager)->getAll() as $issuer) {
                        $extraCerts->add($issuer);
                        $certificates[] = $issuer;
    
            }
    
            $module->setExtraCertificates($extraCerts);
    
            $signatureContentLength = $args['body']['signatureLength'] ?? CertificateSignatureController::$signatureLength;
    
            foreach ($extraCerts->getAll() as $extraCert) {
                $signatureContentLength += (strlen($extraCert->get(\SetaPDF_Signer_X509_Format::DER)) * 2);
    
    
            $user = UserModel::getById(['id' => $GLOBALS['id'], 'select' => ['firstname', 'lastname', 'email']]);
            $signer->setName($user['firstname'] . ' ' . $user['lastname'] . ' ('. $user['email'] . ')');
            $signer->setReason('Signature du document par ' . $user['firstname'] . ' ' . $user['lastname']);
            $signer->setLocation('Maarch Parapheur');
    
    
            $signer->setSignatureContentLength($signatureContentLength);
    
    
            if (!empty($args['body']['signatures'][0])) {
    
                $pages = $document->getCatalog()->getPages();
                $pageCount = $pages->count();
        
                for ($pageNumber = 1; $pageNumber <= $pageCount; $pageNumber++) {
                    $page = $pages->getPage($pageNumber);
        
                    $format = \SetaPDF_Core_PageFormats::getFormat($page->getWidthAndHeight(), \SetaPDF_Core_PageFormats::ORIENTATION_AUTO);
    
    
                    $signature = $args['body']['signatures'][0];
    
                    if ($signature['page'] == $pageNumber) {
                        $image = base64_decode($signature['encodedImage']);
                        if ($image === false) {
                            return ['errors' => 'base64_decode failed'];
                        }
        
                        $imageTmpPath = $tmpPath . $GLOBALS['id'] . '_' . rand() . '_writing.png';
                        if ($signature['type'] == 'SVG') {
                            $Imagick = new \Imagick();
                            $Imagick->readImageBlob($image);
                            $Imagick->setImageFormat("png24");
                            $Imagick->writeImage($imageTmpPath);
                            $Imagick->clear();
                            $Imagick->destroy();
                        } else {
                            file_put_contents($imageTmpPath, $image);
                            if ($signature['positionX'] == 0 && $signature['positionY'] == 0) {
                                $signWidth = $format['width'];
                                $signPosX  = 0;
                                $signPosY  = 0;
                            } else {
                                $signWidth = ($signature['width'] * $format['width']) / 100;
                                $signPosX  = ($signature['positionX'] * $format['width']) / 100;
                                $signPosY  = ($signature['positionY'] * $format['height']) / 100;
                            }
                            $signatureInfo = [
    
                                'page'          => $signature['page'],
                                'positionX'     => $signPosX,
                                'positionY'     => $signPosY,
                                'filePath'      => $imageTmpPath,
                                'signWidth'     => $signWidth
                            ];
    
                        }
                    }
                }
            }
    
            if (!empty($signatureInfo)) {
                $image = \SetaPDF_Core_Image::getByPath($signatureInfo['filePath']);
                $imageXObject = $image->toXObject($document);
                $width = $signatureInfo['signWidth'];
                $height = $imageXObject->getHeight($width);
    
                $fieldName = $signer->addSignatureField(
                    \SetaPDF_Signer_SignatureField::DEFAULT_FIELD_NAME,
                    $signatureInfo['page'],
                    \SetaPDF_Signer_SignatureField::POSITION_LEFT_TOP,
                    ['x' => $signatureInfo['positionX'], 'y' => -$signatureInfo['positionY']],
                    $width,
                    $height + 50
                )->getQualifiedName();
    
                $signer->setSignatureFieldName($fieldName);
    
                $xObject = \SetaPDF_Core_XObject_Form::create($document, [0, 0, $width, $height + 50]);
                $canvas = $xObject->getCanvas();
                $imageXObject->draw($canvas, 0, 50, $width, $height);
    
                $font = new \SetaPDF_Core_Font_Type0_Subset(
                    $document,
                    __DIR__ . '/fonts/dejavu-fonts-ttf-2.37/ttf/DejaVuSans.ttf'
                );
    
                $textBlock = new \SetaPDF_Core_Text_Block($font, 7);
                $textBlock->setWidth($width);
                $textBlock->setLineHeight(14);
                $textBlock->setPadding(2);
    
                $textBlock->setText("Signé électroniquement par : " . $user['firstname'] . ' ' . $user['lastname'] . "\nLe " . date('c'));
    
                $textBlock->draw($canvas, 0, 30);
    
    
                $appearance = new \SetaPDF_Signer_Signature_Appearance_XObject($xObject);
                $signer->setAppearance($appearance);
            } else {
                $fieldName = $signer->addSignatureField()->getQualifiedName();
                $signer->setSignatureFieldName($fieldName);
            }
    
            $tempPath = \SetaPDF_Core_Writer_TempFile::createTempPath();
    
            $_SESSION['tmpDocument'] = $signer->preSign(
    
                new \SetaPDF_Core_Writer_File($tempPath),
                $module
    
            $_SESSION['module'] = $module;
    
                'dataToSign'                => \SetaPDF_Core_Type_HexString::str2hex($module->getDataToSign($_SESSION['tmpDocument']->getHashFile())),
    
                'signatureContentLength'    => $signatureContentLength,
    
                'signatureFieldName'        => $fieldName,
                'tmpUniqueId'               => $args['body']['tmpUniqueId'] ?? null
    
        public static function signDocument(array $args)
    
            session_start();
    
    
            $certificateSignature   = \SetaPDF_Core_Type_HexString::hex2str($args['hashSignature']);
            $tmpPath                = CoreConfigModel::getTmpPath();
    
            if (!empty($args['tmpUniqueId'])) {
    
                if (is_file("{$tmpPath}tmpSignatureEsign_{$GLOBALS['id']}_{$args['tmpUniqueId']}.pdf")) {
                    $pathToDocument = "{$tmpPath}tmpSignatureEsign_{$GLOBALS['id']}_{$args['tmpUniqueId']}.pdf";
    
                }
            }
    
            if (empty($pathToDocument)) {
    
                $adr = AdrModel::getDocumentsAdr([
    
                    'select'  => ['path', 'filename'],
                    'where'   => ['main_document_id = ?', 'type = ?'],
                    'data'    => [$args['id'], 'ESIGN']
    
                if (!empty($adr)) {
    
                    $docserver = DocserverModel::getByType(['type' => 'ESIGN', 'select' => ['path']]);
    
                } else {
                    $adr = AdrModel::getDocumentsAdr([
                        'select' => ['path', 'filename'],
                        'where'  => ['main_document_id = ?', 'type = ?'],
                        'data'   => [$args['id'], 'DOC']
                    ]);
    
                    $docserver = DocserverModel::getByType(['type' => 'DOC', 'select' => ['path']]);
    
                $pathToDocument = $docserver['path'] . $adr[0]['path'] . $adr[0]['filename'];
    
            $signedDocumentPath = $tmpPath . $GLOBALS['id'] . '_' . rand() . '_signedDocument.pdf';
            $writer             = new \SetaPDF_Core_Writer_File($signedDocumentPath);
            $document           = \SetaPDF_Core_Document::loadByFilename($pathToDocument, $writer);
            $signer             = new \SetaPDF_Signer($document);
    
    
            $_SESSION['module']->setSignatureValue($certificateSignature);
            $cms = (string)$_SESSION['module']->getCms();
    
            $signatureContentLength = $args['signatureContentLength'];
    
                $signer->saveSignature($_SESSION['tmpDocument'], $cms);
    
                // unlink($tmpDocument->getWriter()->getPath());
    
            } catch (\SetaPDF_Signer_Exception_ContentLength $e) {
    
                // unlink($tmpDocument->getWriter()->getPath());
    
                $signatureContentLength = $signatureContentLength + 1000;
    
                return ['errors' => 'Not enough space for signature', 'newSignatureLength' => $signatureContentLength];
    
            if ($args['signatureMode'] == 'rgs_2stars_timestamped') {
    
                $document = DocumentModel::getById(['select' => ['digital_signature_transaction_id'], 'id' => $args['id']]);
                $config = DigitalSignatureController::getConfig();
                $signedDocumentPath = DigitalSignatureController::timestampHashes([
                    'config'             => $config,
                    'signedDocumentPath' => $signedDocumentPath,
                    'transactionId'      => $document['digital_signature_transaction_id'],
    
                    'fieldName'          => $args['signatureFieldName'],
                    'extraCertificate'   => $args['certificate']
    
                ]);
                DigitalSignatureController::terminate(['config' => $config, 'transactionId' => $document['digital_signature_transaction_id']]);
            }
    
    
    
            // if (!empty($storeInfos['errors'])) {
            //     return ['errors' => $storeInfos['errors']];
            // }
    
    
            if ($args['lastStep']) {
                $storeInfos = DocserverController::storeResourceOnDocServer([
                    'encodedFile'   => base64_encode(file_get_contents($signedDocumentPath)),
                    'format'        => 'pdf',
                    'docserverType' => 'ESIGN'
                ]);
    
                AdrModel::deleteDocumentAdr([
                    'where' => ['main_document_id = ?', 'type = ?'],
                    'data'  => [$args['id'], 'ESIGN']
                ]);
                AdrModel::createDocumentAdr([
                    'documentId'  => $args['id'],
                    'type'        => 'ESIGN',
                    'path'        => $storeInfos['path'],
                    'filename'    => $storeInfos['filename'],
                    'fingerprint' => $storeInfos['fingerprint']
                ]);
            } else {
                copy($signedDocumentPath, "{$tmpPath}tmpSignatureEsign_{$GLOBALS['id']}_{$args['tmpUniqueId']}.pdf");
            }
            unlink($signedDocumentPath);
    
    
            return true;
    
    
        public static function signWithServerCertificate(\setasign\Fpdi\Tcpdf\Fpdi &$pdf)
        {
            $loadedXml = CoreConfigModel::getConfig();
    
            if ($loadedXml->electronicSignature->enable == 'true') {
                $certPath       = realpath((string)$loadedXml->electronicSignature->certPath);
                $privateKeyPath = realpath((string)$loadedXml->electronicSignature->privateKeyPath);
                if (!is_file($certPath) || !is_file($privateKeyPath)) {
                    return ['errors' => 'Signature with server certificate failed : certPath or privateKeyPath is not valid'];
                }
                $certificate = 'file://' . $certPath;
                $privateKey = 'file://' . $privateKeyPath;
                $info = [
                    'Name'        => (string)$loadedXml->electronicSignature->certInfo->name,
                    'Location'    => (string)$loadedXml->electronicSignature->certInfo->location,
                    'Reason'      => (string)$loadedXml->electronicSignature->certInfo->reason,
                    'ContactInfo' => (string)$loadedXml->electronicSignature->certInfo->contactInfo
                ];
                $pdf->setSignature($certificate, $privateKey, (string)$loadedXml->electronicSignature->password, '', 2, $info);
            }
    
            return true;
        }