public function signDocument(&$src_document) { global $mtlda, $audit; if (!is_a($src_document, 'Mtlda\\Models\\DocumentModel')) { static::raiseError(__METHOD__ . ' only supports DocumentModels!'); return false; } $this->sendMessage('sign-reply', 'Retrieving document copy from archive.', '40%'); if (!($fqpn = $src_document->getFilePath())) { static::raiseError(get_class($src_document) . '::getFilePath() returned false!'); return false; } if (!file_exists($fqpn)) { static::raiseError("{$fqpn} does not exist!"); return false; } if (!is_readable($fqpn)) { static::raiseError("{$fqpn} is not readable!"); return false; } if (!$src_document->getSigningIconPosition()) { static::raiseError("document_signing_icon is not set!"); return false; } try { $audit->log("signing request", "request", "signing", $src_document->getGuid()); } catch (\Exception $e) { $signing_item->delete(); static::raiseError("AuditController::log() raised an exception!"); return false; } if (($public_key = file_get_contents($this->pdf_cfg['certificate'])) === false) { static::raiseError("reading {$this->pdf_cfg['certificate']} failed!"); return false; } if (!($public_key = preg_replace('/(\\s*)-----(\\s*)(BEGIN|END) CERTIFICATE(\\s*)-----(\\s*)/', '', $public_key))) { static::raiseError("failed to strip RSA headers!"); return false; } if (!($public_key = str_replace("\n", '', $public_key))) { static::raiseError("failed to strip whitespaces from public key!"); return false; } $this->sendMessage('sign-reply', 'Sending SOAP request to signing server ' . $this->pdf_cfg['dss_url'] . '.', '50%'); try { $dss = new \SoapClient($this->pdf_cfg['dss_url'] . '/wservice/signatureService?wsdl', array('soap_version' => SOAP_1_1, 'trace' => 1, 'exceptions' => true, 'cache_wsdl' => WSDL_CACHE_NONE)); } catch (\DSSException $d) { static::raiseError($d); return false; } catch (\SOAPFault $f) { static::raiseError($f->faultcode . ' - ' . $f->faultstring); return false; } catch (\Exception $e) { static::raiseError("Failed to load SoapClient!"); return false; } if (!is_callable(array($dss, "getDataToSign"))) { static::raiseError("Remote side does not provide getDataToSign() method!"); return false; } if (!is_callable(array($dss, "signDocument"))) { static::raiseError("Remote side does not provide signDocument() method!"); return false; } $parameters = new \stdClass(); $document = new \stdClass(); $parameters->asicZipComment = false; $parameters->chainCertificateList = array(); $parameters->chainCertificateList[] = array('signedAttribute' => 'true', 'x509Certificate' => base64_decode($public_key)); if (isset($this->tsp_cfg['tsp_ca_certificate']) && !empty($this->tsp_cfg['tsp_ca_certificate'])) { $parameters->chainCertificateList[] = array('signedAttribute' => 'true', 'x509Certificate' => base64_decode($this->tsp_cfg['tsp_ca_certificate'])); } $parameters->deterministicId = $src_document->getGuid(); $parameters->signatureLevel = 'PAdES_BASELINE_LTA'; $parameters->signaturePackaging = 'ENVELOPED'; $parameters->digestAlgorithm = $this->pdf_cfg['signature_algorithm']; $parameters->encryptionAlgorithm = 'RSA'; $parameters->timestampDigestAlgorithm = $this->tsp_cfg['tsp_algorithm']; $parameters->signWithExpiredCertificate = false; $parameters->signingCertificateBytes = base64_decode($public_key); $parameters->signerLocation = array('city' => 'Obersdorf', 'country' => 'Austria', 'postalAddress' => 'Schloßpark 5/12/5', 'postalCode' => '2120', 'stateOrProvince' => 'Lower Austria'); $parameters->signingDate = time(); // set document information /*$pdf->SetCreator(PDF_CREATOR); $pdf->SetAuthor($this->pdf_cfg['author']); $pdf->SetTitle('Test Title'); $pdf->SetSubject('Test Subject'); $pdf->SetKeywords('Test, Keywords'); // set additional information $info = array( 'Name' => $this->pdf_cfg['author'], 'Location' => $this->pdf_cfg['location'], 'Reason' => $this->pdf_cfg['reason'], 'ContactInfo' => $this->pdf_cfg['contact'], ); */ if (!($document->bytes = file_get_contents($fqpn))) { static::raiseError("Failed to read {$fqpn}."); return false; } $document->name = basename($fqpn); $document->mimeType = new \stdClass(); $document->mimeType->mimeTypeString = 'application/pdf'; $document->absolutePath = $fqpn; $this->sendMessage('sign-reply', 'Submitting document to signing server ' . $this->pdf_cfg['dss_url'], '60%'); try { $result = $dss->getDataToSign(array('document' => $document, 'wsParameters' => $parameters)); } catch (\SoapFault $f) { static::raiseError($f->faultcode . ' - ' . $f->faultstring . '<br />' . htmlspecialchars($dss->__getLastRequest())); return false; } catch (\Exception $e) { static::raiseError("SOA getDataToSign() method returned unexpected!"); return false; } if (!isset($result) || empty($result) || !isset($result->response) || empty($result->response)) { static::raiseError("Invalid response on SOAP request 'getDataToSign'!"); return false; } if (!isset($this->pdf_cfg['password']) || empty($this->pdf_cfg['password'])) { $this->pdf_cfg['password'] = false; } if (!($key = openssl_pkey_get_private($this->pdf_cfg['private_key'], $this->pdf_cfg['password']))) { static::raiseError("Failed to read private key!"); return false; } if (!openssl_sign($result->response, $signature, $key, $this->tsp_digest_algorithm)) { openssl_free_key($key); static::raiseError("openssl_sign() returned false!"); return false; } unset($result); openssl_free_key($key); if (!isset($signature) || empty($signature)) { static::raiseError("openssl_sign() returned invalid signature!"); return false; } $this->sendMessage('sign-reply', 'Now signing the signing servers response digest.', '70%'); try { $result = $dss->signDocument(array('document' => $document, 'wsParameters' => $parameters, 'signatureValue' => $signature)); } catch (\SoapFault $f) { static::raiseError($f->faultcode . ' - ' . $f->faultstring . '<br />' . htmlspecialchars($dss->__getLastRequest())); return false; } catch (\Exception $e) { static::raiseError("SOA signDocument() method returned unexpected!"); return false; } if (!isset($result) || empty($result) || !isset($result->response) || empty($result->response)) { static::raiseError("Invalid response on SOAP request 'signDocument'!"); return false; } if (!isset($result->response->bytes) || empty($result->response->bytes) || strlen($result->response->bytes) == 0) { static::raiseError("No document received up on SOAP request 'signDocument'!"); return false; } $this->sendMessage('sign-reply', 'Transfering the signed document into archive.', '80%'); if (file_put_contents($fqpn, $result->response->bytes) === false) { static::raiseError("Failed to write signed document into {$fqpn}!"); return false; } unset($result); return true; }